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

Compare commits

...

451 Commits
1.1.4 ... trunk

Author SHA1 Message Date
cmpilato
4251b52987 Fix buglet in (unused) ViewVCHtmlFormatter.get_result() function.
* lib/viewvc.py
  (ViewVCHtmlFormatter.get_result): Remove some unnecessary
    statements, and fix the fact that this was disregarding the 'maxlen'
    parameter.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2905 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-07-10 16:29:13 +00:00
cmpilato
0ac2f63f71 * conf/viewvc.conf.dist: Minor comment formatting tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2904 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-05-01 15:49:29 +00:00
cmpilato
aa724feae4 Fix issue #527 ("ViewVC markup/annotate views raise exceptions
depending on Python version").

* lib/viewvc.py
  (transcode_text): Avoid using keyword arguments with unicode() and
    .encode(), support for which wasn't added until Python 2.7.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2902 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-25 14:52:58 +00:00
cmpilato
9e97bd332d * CHANGES
Merge 1.1.20 changes.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2901 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-24 14:12:41 +00:00
cmpilato
89d478a933 Bump latest advertised release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2899 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-24 13:31:36 +00:00
cmpilato
a1fd08948a * lib/viewvc.py
(markup_stream, DiffSource._format_text): Fix still more buglets
    around tab-to-space conversion.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2895 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-23 20:17:01 +00:00
cmpilato
d2e7f4aff2 * lib/vclib/ccvs/__init__.py
(find_root_in_parent): Minor comment tweak only.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2893 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-23 18:53:15 +00:00
cmpilato
ece31e7369 * lib/vclib/ccvs/__init__.py
(_is_cvsroot): New helper function.
  (expand_root_parent, find_root_in_parent): Rework these to now
    use _is_cvsroot().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2892 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-23 18:49:10 +00:00
cmpilato
16d7e0af8a Fix issue #526 ("Subversion roots not longer accessible").
This is a big-time regression in root handling which unfortunately
found its way into the 1.1.19 release.

Reported by: Michael Theys <michael.th90{_AT_}gmail.com>,
             olli hauer <ohauer{_AT_}gmx.de>

* lib/vclib/ccvs/__init__.py
  (expand_root_parent): Avoid unnecessary convenience variable.
  (find_root_in_parent): Don't return a rootpath that we've not
    checked for existence!!


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2891 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-23 18:43:15 +00:00
cmpilato
75e503a16b * CHANGES
Merge in 1.1.19 changes.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2888 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-22 19:28:18 +00:00
cmpilato
3fbaffa2fd Bump latest advertised release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2885 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-22 19:20:06 +00:00
cmpilato
a4ad1b13c2 * lib/viewvc.py
(make_time_string): A little syntax change to be more Pythonic.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2881 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-19 20:34:46 +00:00
cmpilato
1f58e41376 Finish issue #46 ("ISO 8601 date/time format").
* lib/viewvc.py
  (make_time_string): If cfg.options.iso8601_timestamps is set,
    generate ISO-8601-compliant timestamp strings.

* lib/config.py
  (Config.set_defaults): Initialize 'iso8601_timestamps' option value.

* conf/viewvc.conf.dist
  (iso8601_timestamps): New option.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2880 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-19 20:31:15 +00:00
cmpilato
4fd9cc0811 Fix some latent bugs whitespace handling.
* lib/viewvc.py
  (markup_stream): Only expand tabs if the tabsize > 0.
  (DiffSource._format_text): Only strip EOL stuffs from the ends of
    lines -- preserve other whitespace forms.  Also, only expand tabs if
    the tabsize > 0.

* conf/viewvc.conf.dist
  (tabsize): Use the term "horizontal tab character" rather than
    "tabstop".  The latter is the destination; the former is the
    character that tells the text flow to resume there.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2876 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-18 13:49:15 +00:00
cmpilato
7026dcb498 Fix an exception recently introduced.
* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.filesize): Recombine path_parts before
    calling _get_dirents().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2874 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-17 18:16:48 +00:00
cmpilato
6406b106a1 More work on issue #11 ("Universal UTF-8 output from ViewVC").
* lib/viewvc.py
  (markup_stream): When transcoding, handle the whole body of file
    contents at once rather than trying to go line-by-line.  Why?
    Because until transcoded, the concept of a "line" is rather
    meaningless.  (NOTE: There is almost certainly some fallout to
    occur in the annotate view as a result of this work, since each VC
    system's annotation support also uses the naive concept of a
    line.)

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2873 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-17 17:59:41 +00:00
cmpilato
a2090ce90b Make some additional dents on issue #11 ("Universal UTF-8 output from
ViewVC").

Use the 'chardet' module where available and enabled by configuration
to detect source file content encoding, and transcode the output to
UTF-8.  We supported this already via Pygments when
'enable_syntax_coloration' was set; now we can also support this when
it isn't.

* lib/viewvc.py
  (detect_encoding, transcode_text): New helper functions.
  (markup_stream): Use the new helper functions to attempt to
    transcode text into UTF-8 even when syntax coloration is *not* in
    use.

* conf/viewvc.conf.dist
  (detect_encoding): Remove notation about this only being used when
    'enable_syntax_coloration' is also enabled.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2872 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-17 15:23:42 +00:00
cmpilato
0ca6106034 Fix issue #525 ("Tarball output is double-compressed when
allow_compress=1").

* lib/viewvc.py
  (get_writeready_server_file): Add 'allow_compress' parameter, and
    handling to give callers the power to disable response-level
    compression.
  (download_tarball): When calling get_writeready_server_file(),
    disable compression (if any) since we're doing our own gzipping
    herein.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2870 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-16 18:15:50 +00:00
cmpilato
801eaff764 Fix an ancient code concern by no longer reading file content fully
into memory when generating tarballs.

* lib/viewvc.py
  (generate_tarball): Avoid reading file contents fully into memory.
    Rather, query the filesize from the vclib provider and chunk the
    output.  If the filesize must be calculated, use two chunked
    passes over those contents (one to measure, one to write to the
    tarball).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2867 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-15 19:06:30 +00:00
cmpilato
1ca3dc4195 Fix issue #524 ("Give administrators a way to limit the size of files
processed by markup and annotate views").  This introduces a new
'max_filesize_kbytes' configuration option for limiting markup and
annotate operations on really big files (whose contents unfortunately
must be read fully into memory sometimes).  By default, a 512-kilobyte
limit will be in place.

* lib/vclib/__init__.py
  (Repository.filesize): New.

* lib/vclib/svn/svn_repos.py
  (LocalSubversionRepository.filesize): New function.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.filesize): New function.

* lib/vclib/ccvs/bincvs.py
  (BaseCVSRepository.filesize): New function (returns -1 aka "not
    implemented")

* conf/viewvc.conf.dist
  (max_filesize_kbytes): New configuration option.

* lib/config.py
  (Config.set_defaults): Set default value for new
    'max_filesize_kbytes' configuration option.

* lib/viewvc.py
  (assert_viewable_filesize): New helper function.
  (markup_or_annotate): Use assert_viewable_filesize() and the new
    repos.filesize() API to honor the new 'max_filesize_kbytes'
    configuration option.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2866 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-15 19:04:40 +00:00
cmpilato
814bee3e4a Over in Subversion-land, I finally got around to exposing the
svn_cmdline_create_auth_baton() function via the SWIG Python
bindings.  So now use it if it's available!

* lib/vclib/svn/svn_ra.py
  (setup_client_ctx): New compatability-sensitive function.
  (RemoteSubversionRepository.open): Use setup_client_ctx() now.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2864 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-12 15:05:28 +00:00
cmpilato
b67254fcf2 Optimize the mapping of a single root name to a root path by avoiding
a potentially costly directory listing within the root parent paths.
This has been shown to reduce the initial ViewVC startup overhead by
400% in some situations where disk I/O is especially sluggish.

* lib/vclib/ccvs/__init__.py
  (find_root_in_parent): New function.

* lib/vclib/svn/__init__.py
  (find_root_in_parent): New function.

* lib/viewvc.py
  (find_root_in_parents): Use the new find_root_in_parent() functions
    offered by the vclib implementations rather than the more expensive
    full root expansion stuffs.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2862 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-20 13:15:34 +00:00
cmpilato
decb5e375b * lib/viewvc.py
Drop in some more timestamp debugging sections.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2860 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-18 18:38:51 +00:00
cmpilato
af996b9b49 Finish issue #487 ("Preserve Subversion symlinks in generated
tarballs") for remote Subversion repositories, too.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.get_symlink_target): New function.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2858 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-04 20:23:09 +00:00
cmpilato
388c55e564 * lib/vclib/svn/svn_ra.py
(cat_to_tempfile): Renamed from temp_checkout().  Callers updated.
  (RemoteSubversionRepository.openfile): Use cat_to_tempfile() now,
    and try to head off a race condition that could leave tempfiles
    lying about.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2857 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-04 19:57:35 +00:00
cmpilato
ab923539dd Finish issue #487 ("Preserve Subversion symlinks in generated tarballs")
for local Subversion repositories.

* lib/vclib/svn/svn_repos.py
  (LocalSubversionRepository.get_symlink_target): New function.

* lib/viewvc.py
  (generate_tarball_header): Add the ability to generate private
    headers for long symlink names, too.
  (generate_tarball): Use the Repository object's get_symlink_target()
    function (if available) to determine whether a versioned object is a
    symlink, and use that information to preserve symlinks in
    generated tarballs.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2855 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-04 19:37:31 +00:00
cmpilato
701ba24454 * lib/viewvc.py
(generate_tarball_header): Indentation fixes only.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2854 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-04 16:52:19 +00:00
cmpilato
eaeb527802 * lib/viewvc.py
Fix spelling error ('typefrag' should be 'typeflag') throughout.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2853 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-04 16:51:38 +00:00
cmpilato
819915112e * templates/default/docroot/styles.css
Revert r2835.  Not liking the yellow.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2852 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-01 15:52:29 +00:00
cmpilato
33eeb41763 * CHANGES: Merge in 1.1.18's changes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2851 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-28 19:34:22 +00:00
cmpilato
345e7ceb8d Bump the advertised latest release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2849 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-28 19:26:50 +00:00
cmpilato
63563517d7 * bin/make-database
Be less sloppy about what this script reports as default behavior
  around hostnames and ports.  If a hostname isn't supplied,
  "localhost" isn't strictly passed to 'mysql'; same for the port.
  And on UNIX, the lack of an explicit hostname (or an explicit
  "localhost" one) will cause 'mysql' to use a UNIX socket rather than
  a TCP/IP one, regardless of what the provided port is.  So it's not
  strictly accurate to imply that by not providing a hostname and
  port, 'make-database' will use "localhost/3306" for that
  information.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2844 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-26 16:51:03 +00:00
cmpilato
b8a7f32fb4 When querying the commits database, explicitly name columns in case
someone has a modified/customized database with additional or
reordered columns.

* lib/cvsdb.py
  (CheckinDatabase.CheckCommit, CheckinDatabase.PurgeRepository):
    Name columns in SELECT statement rather than using '*'.

Patch by: Grant Bremer <gbremer{__AT__}tigris.org>
          (Tweaked by me.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2843 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-26 16:43:12 +00:00
cmpilato
8854a73040 * bin/make-database
Use "ENGINE=MyISAM" rather than "TYPE=MyISAM" throughout.  The
  latter syntax was deprecated in MySQL 5.0 and dropped in 5.5.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2842 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-26 16:15:35 +00:00
cmpilato
9b57e7d6a6 Fix issue #521 ("make-database does not allow the database port to be
specified").

* bin/make-database
  (__main__): Add a --port option and handling thereof, passing the
    value off to 'mysql'.

Patch by: Grant Bremer <gbremer{__AT__}tigris.org>

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2841 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-25 20:51:01 +00:00
cmpilato
3edd7de4d1 Fix issue #347 ("ccvs module handling of "dead" files doesn't jive
with that of bincvs logic").

* lib/vclib/ccvs/ccvs.py
  (InfoSink.define_revision): Tweak the revision matching logic a bit
    to include branch points as valid revisions when examining branch
    tags.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2839 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-25 20:35:56 +00:00
cmpilato
07ac7f5e54 Mark busted ,v files with no revisions as "absent" in the rcsparse-
driven CVS backend, fixing an inconsistency between rcsparse-based and
binary-based parsing.

* lib/vclib/ccvs/ccvs.py
  (InfoSink.__init__): Init new 'saw_revision' flag.
  (InfoSink.define_revision): Set 'saw_revision'.
  (InfoSink.parse_completed): If 'saw_revision' is not set, set the
    'absent' flag.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2837 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-25 20:01:45 +00:00
cmpilato
41858c6f89 * templates/default/docroot/styles.css
Follow up to r2835, removing an unnecessary "!important" CSS
  declaration.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2836 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-22 16:56:26 +00:00
cmpilato
bbe8b42877 * templates/default/docroot/styles.css
Highlight file content lines in yellow when hovered over.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2835 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-22 16:48:33 +00:00
cmpilato
3d3f677b3c * templates/default/file.ezt
Reduce a bit of redundancy.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2834 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-22 16:44:51 +00:00
cmpilato
572554fa89 * lib/viewvc.py
(markup_stream): Strip EOL characters from the ends of marked-up
    lines.  Templates can re-add line breaks, but they can't easily
    strip them.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2832 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-22 16:34:10 +00:00
cmpilato
281ed5ca84 Correct a royally botched CHANGES entry.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2830 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-01-31 19:49:49 +00:00
cmpilato
bb4a748eda Fix issue #519 ("Stack trace while accessing a BDB repository if last
commit deleted a file or directory").

* lib/vclib/svn/svn_repos.py
  (_get_change_copyinfo): Only call svn_fs_copied_from() on "add" and
    "replace" change items (which might actually be copies).  This
    avoids raising an exception caused by running svn_fs_copied_from()
    on a missing (via deletion) root/path pair.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2827 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-01-29 15:57:57 +00:00
cmpilato
f48add83f7 Bump copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2820 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-01-04 19:01:54 +00:00
cmpilato
b57831ade8 Fix the handling of diff options throughout.
* lib/viewvc.py
  (DiffDescription.__init__): Don't set self.diff_options.
  (DiffDescription.get_content_diff, DiffDescription.get_prop_diff):
    Rename local variable 'options' to 'diff_options', and actually
    populate *it* rather than the otherwise unused (and now removed)
    'diff_options' class variable.
  (DiffDescription._get_diff, DiffDescription._line_idiff_sidebyside,
   DiffDescription._line_idiff_unified, DiffDescription._content_fp,
   DiffDescription._prop_fp): Rename 'options' to 'diff_options'.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2818 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-25 20:21:13 +00:00
cmpilato
19818e269c * lib/viewvc.py
(view_patch): Honor the "show functions" option when generating
    patches, too.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2817 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-25 20:14:16 +00:00
cmpilato
0d1da3c79b * templates/default/include/diff_display.ezt
Show the "extra" diff info stuff, too.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2816 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-25 19:56:52 +00:00
cmpilato
d018467937 Revert r2649, as the default templates *should* support the property
diff stuff now if I recall correctly.

* conf/viewvc.conf.dist,
* lib/config.py
  Use "templates/default" now for the default templates.  Duh.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2815 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-25 19:16:18 +00:00
cmpilato
cea54da382 * CHANGES
Merge 1.1.17 changes.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2812 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-25 14:06:06 +00:00
cmpilato
6fcc2b5992 Bump most recent reported release. Again.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2811 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-25 14:05:24 +00:00
cmpilato
dcb7e17dc6 Fix issue #516 ("Regression: UnboundLocalError: local variable
'log_pagestart' referenced before assignment").

* lib/viewvc.py
  (view_log): Initialize the 'log_pagestart' variable.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2808 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-25 13:46:15 +00:00
cmpilato
e8f975475e Update link to the CHANGES file.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2807 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 18:50:57 +00:00
cmpilato
06910f29e3 * CHANGES
Include 1.1.16 and 1.0.13 changes, too.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2806 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 18:46:52 +00:00
cmpilato
64b0994f2f Bump most stable release version.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2804 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 18:21:47 +00:00
cmpilato
a8d3373025 Fix issue #514 (Simple file view has page title with "Annotation of:").
* templates/classic/file.ezt,
* templates/default/file.ezt
  Set the page's <title> attribute to "Annotation of ..." or "Contents
  of ...", depending on whether the output is annotated.

Patch by: John McNally <jmcnally{__AT__}collab.net>

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2796 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 16:57:59 +00:00
cmpilato
de517ae29c Fix issue #515 ("XSS bug in diff view (CVE-2012-4533)").
* lib/viewvc.py
  (DiffSource._get_row): Pass the "extra" line information through the
    formatter code so that, at a minimum, it's HTML-escaped.

Patch by: Nicolás Alvarez <nicolas.alvarez{__AT__}gmail.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2792 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 13:26:36 +00:00
cmpilato
4a98f512f7 * conf/viewvc.conf.dist
Show the default value of 'hr_funout' as 1 (which matches the
  programmatic default).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2791 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 13:22:14 +00:00
cmpilato
f4945fd7d9 Oops! Fix the diff view I broke in r2784.
* lib/viewvc.py
  (view_diff): Add 'cfg' convenience variable (to avoid a stack trace
    caused by using such a thing a few lines later).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2790 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-03 14:33:38 +00:00
cmpilato
4590ebbd6b Fix issue #512 ("'Select for diffs' does not work across pages").
* lib/viewvc.py
  (view_log): Preserve the 'log_pagestart' query value when generating
    the 'select for diff' links so that clicking the link returns you to
    the same page (modulo repagination due to new commits in the race
    window ... but let's not think about that).  Also, preserve the
    'r1' query parameter when generating the paging form.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2788 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-03 14:22:48 +00:00
cmpilato
ace5d55f0e * bin/cgi/query.cgi,
* bin/cgi/viewvc.cgi
  Add some commented-out logic for renice-ing the ViewVC CGI process.

Inspired by:  Karl Berry <karl{__AT__}freefriends.org>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2786 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-09-05 15:52:27 +00:00
cmpilato
00feefc3dc Finish issue #510 ("Block the expensive display of binary files").
Add a new configuration option 'binary_mime_types' which accepts a
comma-delimited list of MIME content type patterns ('text/plain', or
'image/*', etc.) against which versioned file MIME types are
compared for the purposes of deciding whether to allow their display
in the 'markup', 'annotate', 'diff' and 'patch' views.

* conf/viewvc.conf.dist
  (binary_mime_types): Describe new option.

* lib/config.py
  (_force_multi_value): Add 'binary_file_types' to the list of
    multi-value options.
  (Config.set_defaults): Initialize cfg.options.binary_mime_types.

* lib/viewvc.py
  (is_binary_file_mime_type): New function.
  (get_file_view_info): Use is_binary_file_mime_type() to determine
    whether to return links to content-ful views of the input file.
  (markup_or_annotate, view_diff): Use is_binary_file_mime_type() to
    deny display of so-deemed binary files.



git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2784 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-09-05 12:55:19 +00:00
cmpilato
adfa50c9b4 * lib/vclib/svn/svn_ra.py
(RemoteSubversionRepository.itemlog): Only fetch lock and size
    information for files, not directories.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2781 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-22 20:22:49 +00:00
cmpilato
5e815bc101 * docs/release-notes/1.1.0.html
Update some of the warnings with notes about their subsequent
  resolutions.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2780 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-22 19:30:10 +00:00
cmpilato
ca040b2361 Merge changes from 1.1.15 release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2779 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-22 19:29:31 +00:00
cmpilato
b5d66b7afe Bump latest advertised release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2777 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-22 18:52:43 +00:00
cmpilato
1a6390d78a * templates/default/docroot/images/lock-icon.gif
New icon file.

* templates/default/directory.ezt,
* templates/default/docroot/styles.css
  Add template bits for displaying a lock-icon "overlay" for locked
  items in the directory view.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2774 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-19 20:01:39 +00:00
cmpilato
698e9db4f2 * lib/viewvc.py
(LogFormatter.get): Fix a regression introduced in 1.1.14's handling
    of log messages when not HTML-ifying them (for example, when serving
    them up via RSS).

Patch by: Christoph Sommer <christoph.sommer{__AT__}uibk.ac.at>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2771 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-19 18:45:36 +00:00
cmpilato
163f63ed15 Fix some breakage in the annotate view for remote SVN repositories.
* lib/viewvc.py
  (markup_or_annotate): Don't forget to cross copies when requesting
    the item's log.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.itemlog): Treat 'svn_latest_log' option
    specially, as in the svn_repos case.  (Ugh.)
  (RemoteSubversionRepository.annotate): Pass 'svn_show_all_dir_logs'
    option to itemlog().  Also, upgrade to svn_client_blame2() API.
  (RemoteSubversionRepository._get_last_history_rev): Now return both
    the info-provided create_rev, and the more accurate
    last_history_rev.  Callers updated.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2770 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-19 15:26:41 +00:00
cmpilato
44ec30da8e Abstract some code into a helper function, and fix some brokenness therein.
* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository._revinfo_fetch): New.  Holds the guts of
    the revision info harvesting (plus some bugfixes thereto) from...
  (RemoteSubversionRepository._revinfo): ...this, which now calls the
    helper function.  Also, don't tuple-ize the already-tupled cache
    info.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2768 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 01:38:39 +00:00
cmpilato
1380dba9ba Normalize some variables before hitting the Subversion API.
* lib/vclib/svn/svn_ra.py
  (client_log): Convert incoming 'cross_copies' and 'include_changes'
    values to integers.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2767 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 01:36:11 +00:00
cmpilato
6533b4fc7c Optimize revision info fetches for remote SVN repositories to avoid
unnecessary (and somewhat expensive) work.

* lib/vclib/svn/svn_ra.py
  (client_log): Add 'include_changes' parameter, pass to the
    Subversion log APIs.  Callers updated.
  (_revinfo): Was _revinfo_raw().  Add 'include_changed_paths'
    parameter.  Now handles the revinfo cache, only fetches changed
    paths when required, and bails out early of authz checks when
    possible.  All internal callers of revinfo() have been updated to
    use this interface instead.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2765 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 01:13:25 +00:00
cmpilato
6964b067e6 Fix a couple of correctness/performance regressions in the remote SVN
annotate view recently introduced.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.annotate): Pass 'svn_cross_copies'
    option to itemlog() so that annotation history isn't unnaturally
    truncated.
  (RemoteSubversionRepository._blame_cb): Only consult the revinfo
    cache when authz checks are required.  This improves the speed of
    the operation when universal read access is granted to the user.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2763 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 00:51:25 +00:00
cmpilato
fee21df5d7 Finish, I believe, issue #353 ("Remote Subversion repositories not
fully honoring authz rules").  In doing so, this makes the svn_ra
module the most accurate it has ever been, hopefully without too
terribly much extra cost.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.listdir): No longer check access here.
    That's handled down in _get_dirents() now.
  (RemoteSubversionRepository.dirlogs): Don't check dirent access here.
    Just ensure that the returned entries are those which are
    represented in the (filtered) set return from _get_dirents().
    Also, replace the revision metadata stored on each dirent with
    values from the revinfo cache (which does authz sanitizing).
  (RemoteSubversionRepository._get_dirents): Do authz-checking of
    dirents here, and use _get_last_history_rev() to populate a useful
    created_rev.
  (RemoteSubversionRepository._get_last_history_rev): Because
    Subversion's RA layer doesn't consider copy events when
    determining an item's last-committed-rev, compensate for this with
    a bounded log operation which does.  This brings created-rev
    parity with the svn_repos module (at the cost of the extra RA
    work, but with the benefit of, you know, accuracy.
  (RemoteSubversionRepository._revinfo_raw): Leave a TODO about a
    future optimization.
  (RemoteSubversionRepository._log_cb): Set found_unreadable when
    sanitizing an unreadable copyfrom path, too.
  (RemoteSubversionRepository.created_rev): Now just a thin wrapper
    around the beefed-up _get_last_history_rev() function.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2760 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-15 23:34:01 +00:00
cmpilato
ada79bc6e7 * lib/vclib/svn/svn_ra.py
(LogCollector.add_log): Allow the access_check_func to be None (and
    avoid trying to call it in that case).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2759 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-15 23:07:22 +00:00
cmpilato
01ebe77beb Fix a security issue: When a readable path is copied from an
unreadable one, Subversion will obscure the fact that the operation
was a copy (by removing copyfrom info) and will deem the log message
for the revision in which the copy occurred to be unreadable.  ViewVC
was only doing the former bit; now it does the latter, too.

* lib/vclib/svn/svn_repos.py
  (LocalSubversionRepository._get_changed_paths): Set found_unreadable
    when we have to hide a copyfrom path, too.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2758 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-15 23:05:47 +00:00
cmpilato
87b574b3ee For issue #353, check path access in the annotation view code for
remote SVN repositories.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.annotate): Examine revision logs to
    determine the legal annotation range we are allowed to request,
    and use the revinfo cache to strip sensitive author/data info from
    the annotation data.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2757 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-15 02:17:55 +00:00
cmpilato
fc7d9b1da4 For issue #353 ("Remote Subversion repositories not fully honoring
authz rules"):

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.get_locations): Check authz on the
    result of a get_locations lookup (being sure to use the original
    path in the error message, if any).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2756 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-15 02:11:12 +00:00
cmpilato
4cc840b54a For issue #353, check path access in the code that backs the revision
log view for remote SVN repositories.

* lib/vclib/svn/svn_ra.py
  (LogCollector.__init__): Add 'access_check_func' parameter, stashed
    away as a member variable.  Initialize 'done' variable to False.
  (LogCollector.add_log): Use access_check_func() to test for access.
    If access is denied, set 'done' to True and ignore future
    invocations.
  (RemoteSubversionRepository.itemlog): Add _access_checker inner
    function, passed as a callback to LogCollector.__init__().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2755 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-13 21:57:38 +00:00
cmpilato
3df47b625e * notes/releases.txt
Add some URLs for the announce@ and freecode postings.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2753 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 13:02:34 +00:00
cmpilato
836e643dc5 * CHANGES: Merge in the 1.1.14 changes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2752 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 12:57:12 +00:00
cmpilato
ca52fe277d Bump latest release version number.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2750 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 12:54:04 +00:00
cmpilato
bfe2cbe5ca Fix issue #246 ("Add support for issue tracker links in commit-log
viewer").

NOTE: There are some limitations here, most prominantly that commas
can't be used in the regular expressions which define replacements,
and that only match groupings 0-9 can be used in the replacement
format string.

* conf/viewvc.conf.dist
  (custom_log_formatting): New configuration option.

* lib/config.py
  (Config._force_multi_value, Config.set_defaults): Add handling of
    new 'custom_log_formatting' option.

* lib/viewvc.py
  (ViewVCHtmlFormatter.format_custom_url): New formatter callback.
  (LogFormatter.get): Register the new formatter callback for rules
    found in the 'custom_log_formatting' option value.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2746 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 11:11:18 +00:00
cmpilato
91654688a1 * templates/default/file.ezt
Fix some markup validation issues.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2743 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-04-20 21:04:14 +00:00
cmpilato
9d75ed19f8 Finish issue #506 ("Log message tokenization results could be reused
for better performance").

* lib/viewvc.py
  Replace uses of format_log() throughout with calls to LogFormatter()
  (to get a formatter "lf") then lf.get() (to get the formatted log
  string).  Also...
  (format_log): Remove as unused.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2741 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-04-17 15:29:27 +00:00
cmpilato
f65b03fcdc For issue #506 ("Log message tokenization results could be reused for
better performance"), make the first step of two in trying to avoid
parsing log messages twice when once will suffice.

* lib/viewvc.py
  (ViewVCHtmlFormatterTokens): New class for generating HTML-formatted
    text from a set of provided ViewVCHtmlFormatter tokens.
  (ViewVCHtmlFormatter.get_result): Track renamed function, and defer
    the work of the text generation to the token object's get_result()
    method.
  (ViewVCHtmlFormatter.tokenize_text): Renamed from _tokenize_text(),
    and now return a ViewVCHtmlFormatterTokens object.
  (LogFormatter): New class for encapsulating the log formatting logic.
  (format_log): Make a wrapper around LogFormatter for now.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2740 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-04-17 15:25:56 +00:00
cmpilato
60b80bd3b2 Fix issue #505 ("Username case normalization breaks when browsing
anonymously").

* lib/vcauth/svnauthz/__init__.py
  (ViewVCAuthorizer.__init__): Don't try to convert a None username to
    lower- or upper-case.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2738 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-03-28 14:15:51 +00:00
cmpilato
272ae0fa37 Remove erroneous CHANGES entry.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2736 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-02-07 21:10:18 +00:00
cmpilato
23714979e4 Follow-up to r2512, trying to, you know, get that change right this
time.

* lib/vclib/svn/__init__.py
  (_canonicalize_path): New helper function.
  (canonicalize_rootpath): Rework this to perform the URL -> absolute
    path conversion any time it can, not only when using really old
    Subversion versions.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2735 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-02-07 21:10:04 +00:00
cmpilato
1b63870590 Follow-up to r2731, trying to ensure that users with pre-1.4 Subversion
won't hit AttributeError's on account of svn_path_canonicalize() not 
being exposed through Subversion's Python bindings.

* lib/vclib/svn/svn_repos.py
  (_canonicalize_path): New helper function.
  (_rootpath2url): Use _canonicalize_path() instead of
    core.svn_path_canonicalize().

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository._geturl): Use (imported)
    _canonicalize_path() instead of core.svn_path_canonicalize().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2733 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-02-01 19:45:35 +00:00
cmpilato
1296106171 Fix issue #504 ("annotate view fails on filenames with colon (:)
characters in their names"), another in a long string of annotate view
brokennesses.  (Is that word?)  This time, the problem was a disparity
between Python's urllib encoding and what Subversion deems canonical.

* lib/vclib/svn/svn_repos.py
  (_rootpath2url): Canonicalize URLs for use with Subversion.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository._geturl): Canonicalize URLs for use with
    Subversion.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2731 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-02-01 16:24:29 +00:00
cmpilato
3297f45285 * lib/vclib/svn/__init__.py
(canonicalize_rootpath): Add a TODO comment.



git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2730 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-31 22:37:32 +00:00
cmpilato
91d1b75856 * notes/releases.txt
Minor tweaks/corrections to the release procedure.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2729 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 18:58:02 +00:00
cmpilato
8f39d274f5 Merge 1.1.13's CHANGES.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2727 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 18:52:01 +00:00
cmpilato
86449bdec7 Bump pointer to latest version.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2726 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 18:51:45 +00:00
cmpilato
7043120d2c * tools/bump-copyright-years
(update_license): Remove.
  (bump_years): Print reminder about amending the change history
    section of the LICENSE.html file.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2722 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 18:31:20 +00:00
cmpilato
ad0c0dd5fa * tools/bump-copyright-years
(replace_end_year): Remove debugging print statements, and simplify code.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2721 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 17:57:10 +00:00
cmpilato
9e9b82218c Bump copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2720 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 17:52:47 +00:00
cmpilato
85df9e6ba4 * tools/bump-copyright-years
New tool for bumping copyright years.  I'm tired of trying to
  micromanage these things.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2719 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 17:51:58 +00:00
cmpilato
8fc1166f2d Bump copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2716 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 16:42:03 +00:00
cmpilato
25ab342bbf * lib/vclib/svn/svn_repos.py
(_rootpath2url): Get the absolute path before splitting off the
    drive, not afterward.  Otherwise, (some) drive letter will just get
    tacked back onto the path!  As we now verify elsewhere that
    rootpaths aren't relative, we needn't worry that .splitdrive() will
    return a relative rootpath.

Suggested by: Jens Peters <jp7677{__AT__}gmail.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2714 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-06 21:34:58 +00:00
cmpilato
ccb7f2e3f1 Ensure that we are dealing with absolute repository paths. Who knows
what kind of chaos may ensue otherwise.

* conf/viewvc.conf.dist
  Note that paths should be absolute filesystem paths.

* lib/vclib/svn/__init__.py
  (canonicalize_rootpath, expand_root_parent): Assert that local
    repository paths are absolute.

* lib/vclib/ccvs/__init__.py
  (canonicalize_rootpath, expand_root_parent): Assert that local
    repository paths are absolute.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2713 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-05 15:35:39 +00:00
cmpilato
0cd9f664d6 * INSTALL
Per issue #474, describe how to setup static icon/stylesheet
  delivery, warning about alias ordering.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2712 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-04 17:19:27 +00:00
cmpilato
6fe390a642 Move a customization out of ezt.py to eliminate unnecessary deltas
against the upstream version of this module.

* lib/common.py
  (TemplateData): Moved here...

* lib/ezt.py
  ...from here.

* lib/query.py,
* lib/viewvc.py
  Import 'TemplateData' from 'common' now, and update references to this
  relocated class.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2711 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-04 15:35:12 +00:00
cmpilato
5d68a5f883 * lib/vclib/svn/svn_repos.py
(_rootpath2url): "drive" already carries the colon, so don't append one.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2709 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-03 20:07:30 +00:00
cmpilato
6418278d55 * lib/vclib/svn/svn_repos.py
(_rootpath2url): Try a different approach that should actually
    handle spaces in the "in-repos" path, too, and maybe (if I'm super
    lucky) also work on Windows.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2707 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-03 15:18:21 +00:00
cmpilato
1dd1a21537 * lib/viewvc.py
(): Stop importing the pygments stuff at the module-global scope,
    and go back to doing so...
  (markup_stream): ...here.
  (markup_or_annotate): No longer consider access to Pygments when
    making the first pass at colorizing -- if we don't have it,
    markup_stream() will fail quickly and we can take the second
    (non-colorizing) pass.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2702 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-12 20:01:30 +00:00
cmpilato
06a3feec90 Reintegrate the 'issue-495-dev' branch into the trunk. This
reintroduces the issue #495 feature ("Syntax highlight/colorize
scripts without extensions") while building much more fault tolerance
into the system.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2699 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-06 14:22:48 +00:00
cmpilato
a03cd82de5 On the 'issue-495-dev' branch, make what I think are some pretty
useful improvements to the syntax coloration and annotation codepaths.
First, settle on the file contents as fetched via repos.openfile() as
the canonical source of such.  But also, allow any exceptions thrown
while colorizing those file contents to trigger a second attempt
without colorization enabled.  This should allow for non-error-ful
display of binary files which lack both an extension and VC content
type hint (those are treated by default as text files).

* lib/viewvc.py
  (markup_stream): Add docstring.  Replace 'fp' parameter with
    'file_lines' parameter.  Add 'colorize' parameter.  No longer try
    to munge 'blame_data' -- expect that callers have done that.  Lose
    first_line tracking stuff, as fetching the first line of the file
    is no longer destructive.  Always use 'file_lines' as the source
    of file contents; never the text attached to the 'blame-data'.
  (markup_or_annotate): Rework this to do more of the cheap work of
    annotating and markup up so that markup_stream() can be called
    more than once if necessary -- once to attempt colorization, and
    again without that feature if the first pass fails.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-495-dev@2698 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-05 21:11:29 +00:00
cmpilato
094024a0ec On the 'issue-495-dev' branch:
* lib/vclib/__init__.py
  (Repository.annotate): Rewrite docstring for clarity.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-495-dev@2697 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-05 20:06:11 +00:00
cmpilato
56e645ecb8 ### NOTE FROM THE FUTURE: This commit probably broke the ###
###  "annotate view without syntax coloration" codepath.   ###

On the 'issue-495-dev' branch:  Let vclib API consumers request that
annotation data come with or without the associated text.

* lib/vclib/__init__.py
  (Repository.annotate): Add optional 'include_text' parameter.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.annotate): Add 'include_text' parameter
    and, if False, have the annotation callback drop the text on the floor.

* lib/vclib/svn/svn_repos.py
  (BlameSource.__init__): Add 'include_text' param; squirrel away the value.
  (BlameSource._blame_cb): Don't keep text we're asked not to keep.
  (LocalSubversionRepository.annotate): Add 'include_text' parameter,
    passed to BlameSource.__init__.

* lib/vclib/ccvs/bincvs.py
  (BinCVSRepository.annotate): Add 'include_text' parameter, passed to
    blame.BlameSource().

* lib/vclib/ccvs/ccvs.py
  (CCVSRepository.annotate): Add 'include_text' parameter, passed to
    blame.BlameSource().

* lib/vclib/ccvs/blame.py
  (BlameSource.__init__): Add 'include_text' param; squirrel away the value.
  (BlameSource.__getitem__): Don't keep text we're asked not to keep.

* lib/blame.py
  (HTMLBlameSource.__init__): Update call to repos.annotate().



git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-495-dev@2696 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-05 19:58:38 +00:00
cmpilato
2f722f39b9 On the 'issue-495-dev' branch:
* lib/viewvc.py
  (markup_stream): Was markup_stream_pygments().  Caller changed.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-495-dev@2695 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-05 18:31:55 +00:00
cmpilato
402dfd831f On the 'issue-495-dev' branch:
* lib/viewvc.py
  (markup_stream_pygments): Remove level of indentation made
    unnecessary by r2693.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-495-dev@2694 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-05 18:30:50 +00:00
cmpilato
738d17f71f On the 'issue-495-dev' branch:
* lib/viewvc.py
  (): Try to import the pygments code at the global scope instead of...
  (markup_stream_pygments): ...here.  Add/move some comments, too.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-495-dev@2693 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-05 18:28:05 +00:00
cmpilato
fdc0bcd420 On the issue-495-dev branch: Add BRANCH-README.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-495-dev@2692 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 19:49:45 +00:00
cmpilato
db032720a8 On the 'issue-495-dev' branch: Re-apply the most recently committed
(then reverted) bits of the issue #495 work.

* lib/viewvc.py (markup_stream_pygments)

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-495-dev@2691 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 19:48:24 +00:00
cmpilato
c9256effc4 Create a branch for issue #495 development.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-495-dev@2690 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 19:38:59 +00:00
cmpilato
b42a4a0915 * lib/viewvc.py
(markup_or_annotate): Don't show annotation warnings for markup
    views of images.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2688 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 18:35:01 +00:00
cmpilato
d2275afb6d * lib/viewvc.py
(markup_stream_pygments): Re-make the semantic changes from r2680,
    which were (I believe) good ones, even if the lexer guessing stuff
    hasn't yet work out entirely.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2686 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 18:18:23 +00:00
cmpilato
5db50e6bcc Revert r2657, r2680, and r2681, effectively removing support for
Pygments' lexer guessing functionality (closing the REOPENED bug issue
#501, and necessitating that feature issue #495 be itself REOPENED).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2684 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 17:51:41 +00:00
cmpilato
7b6ecd807c * templates/classic/file.ezt
Say "Content type" rather than "File MIME type".


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2683 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 16:10:57 +00:00
cmpilato
e8a0caeef5 *** THIS REVISION WAS REVERTED IN r2684 ***
For issue #501 ("Stack trace when trying to 'view' binary files"):

* lib/viewvc.py
  (markup_stream_pygments): On second thought, allow guessing to
    happen on non-None MIME types iff they appear to be text-y.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2681 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-29 15:33:20 +00:00
cmpilato
dfeb3b5d90 *** THIS REVISION WAS REVERTED IN r2684 ***
For issue #501 ("Stack trace when trying to 'view' binary files"):

* lib/viewvc.py
  (markup_stream_pygments): Rework the Pygments lexer-choosing logic a
    bit.  First, don't try any of it when syntax highlighting is
    disabled.  Secondly, don't guess at a lexer if we know the file's
    MIME type and have already failed to get a matching lexer.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2680 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-29 15:25:00 +00:00
cmpilato
5943e01304 * lib/viewvc.py
(markup_or_annotate): Pass 'mime_type' to common_template_data() so
    it can properly set the 'prefer_markup' bit.  Otherwise, it will
    guess without out svn:mime-type at its disposal.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2679 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-29 15:17:59 +00:00
cmpilato
6a8d5debd6 Template-y changes for file views.
* templates/default/markup.ezt
  Delete as unused.

* templates/default/include/fileview.ezt
  Delete, after merging contents into...

* templates/default/file.ezt
  ...here.  Also, port the [hide_binary_garbage] stuff from the classic
  templates.

* templates/default/docroot/styles.css
  (.vc_notice): New class.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2678 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-29 14:59:49 +00:00
cmpilato
537c59c516 Port a change from the 'classic' templates to the new 'default' ones.
* templates/default/query_results.ezt
  Display warning text when query results are superficially truncated.

* templates/default/docroot/styles.css
  (.vc_warning): New style.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2676 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-18 20:31:59 +00:00
cmpilato
92b7b65707 Fix annotate views of files in Subversion repository whose paths
contain non-URI-safe characters (spaces, non-ASCII stuff, etc.)

* lib/vclib/svn/svn_repos.py
  (_rootpath2url): Use urllib.pathname2url to URL-encode the path
    portion of the URL we are building.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2674 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-14 21:28:46 +00:00
cmpilato
46e17662d5 Fix issue #499 ("svndbadmin fails on deleted files").
* bin/svndbadmin
  (SvnRev.__init__): Update the logic used to calculate change types
    and diff objects to no longer assume that deleted paths have None
    for their change.path.  (Subversion's 1.7 bindings always populate
    change.path.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2671 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-14 20:14:14 +00:00
cmpilato
7ab960262a Merge in 1.1.12's CHANGES.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2670 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-03 14:45:17 +00:00
cmpilato
6f47cef67d Add a step to the release procedure.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2668 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-03 14:43:57 +00:00
cmpilato
a592e3b0bc Bump latest version number.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2666 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-03 14:39:40 +00:00
cmpilato
94d9f0ef49 * templates/default/log.ezt,
* templates/default/include/pathrev_form.ezt,
* templates/default/directory.ezt,
* templates/default/diff.ezt,
* templates/default/docroot/styles.css
  Still more template stylistic tweaks.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2662 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-01 18:45:27 +00:00
cmpilato
b566f297b3 * templates/default/include/diff_display.ezt
Track recent rename of diffs.display_as to diffs.diff_block_format.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2661 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-01 14:14:40 +00:00
cmpilato
a6be4da978 * templates/default/log.ezt,
* templates/default/docroot/styles.css
  Layout/style tweaks to the new default template set.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2660 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-01 14:12:58 +00:00
cmpilato
8cdccc1ea5 Fix a bug which prevented certain directory diff displays.
* lib/viewvc.py
  (diff_side_item): Pass the option to repos.itemlog() which says to
    include directory logs even for revisions in which the directory
    itself was not explicitly modified (props changed).

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2659 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-01 13:40:01 +00:00
cmpilato
ccb53d164e *** THIS REVISION WAS REVERTED IN r2684 ***
Finish issue #495 ("Syntax highlight/colorize scripts without
extensions").

* lib/viewvc.py
  (markup_stream_pygments): Failing all else, use the Pygments
    guess_lexer() function to guess a file's content type from the
    first line of its text.  (Most of this patch is compensation for
    the first that if this heuristic codepath is traversed, we've eaten
    a line of text from the file object that we don't expect to have
    been eaten.

Patch by: Chris Mayo <cjmayo{__AT__}tigris.org>
          (Tweaked by me.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2657 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-31 21:39:33 +00:00
stilor
441da057cc * lib/viewvc.py
(DiffSource): Do not switch to flush state. Such switch causes changes
  to be displayed as removal and readdition.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2656 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-28 20:37:01 +00:00
cmpilato
a69597e29c Finish issue #470 ("No links to repository root logs").
* lib/viewvc.py
  (view_roots): Generate a log_href -- the revision log for the root
    directory of the repository -- where it makes sense to do so.

* templates/classic/roots.ezt,
* templates/default/roots.ezt
  Markup the last-modified-revision as a link to the log view where we can.

* docs/template-authoring-guide.html
  Note the additional data dictionary item.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2653 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-28 19:24:37 +00:00
cmpilato
43f001ef25 * templates/default/file.ezt,
* templates/default/log.ezt,
* templates/default/docroot/styles.css,
  More markup changes to the default templates.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2652 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-27 21:14:02 +00:00
cmpilato
4341dab5b7 * templates/default/revision.ezt,
* templates/default/log.ezt,
* templates/default/graph.ezt,
* templates/default/diff.ezt,
* templates/default/include/diff_display.ezt
  Take a stab at updating the new default templates to handle the
  property diff stuff.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2651 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-27 20:59:38 +00:00
stilor
6e81b5eb1e * docs/template-authoring-guide.html
* templates/classic/include/diff_display.ezt
* lib/viewvc.py
  Rename 'display_as' to 'diff_block_format', as discussed on ML.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2650 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-27 18:20:06 +00:00
cmpilato
dc1763b37b * lib/config.py,
* conf/viewvc.conf.dist
  Use the "classic" templates until the "default" ones can be updated
  to reflect recent improvements (such as the property-diff stuff).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2649 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-27 18:10:37 +00:00
cmpilato
65137920a8 Begin the work of making the 'newvc' contributed template set into the
new default for ViewVC 1.2.  We'll keep the old templates around as a
"classic" option.

* lib/config.py,
* conf/viewvc.conf.dist
  (Config.set_defaults): Set cfg.options.template_dir to
    "templates/default", and make the matching edit in the pristine
    configuration file.

* templates/:
  Now just a container for...

* templates/classic/,
* templates/classic/*:
  ...the old default templates (which were 'templates' and 'templates/*'.

* templates/default/,
  templates/default/*:
  ...and the new default, copied from the 'templates-contrib/1.1' area.




git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2647 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-27 18:05:14 +00:00
cmpilato
00d96de446 * CHANGES: Note the addition of prop-diff support.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2646 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-27 14:53:49 +00:00
cmpilato
6008bc6a7b Reintegrate the ^/branches/property-diff branch.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2645 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-27 14:51:43 +00:00
cmpilato
753d2d5320 On the 'property-diff' branch:
* docs/template-authoring-guide.html
  Update the template authoring guide in light of the changes I made
  in r2643.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2644 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-26 19:18:20 +00:00
cmpilato
2b5e7b51a0 On the 'property-diff' branch: rename the 'diff' template data
dictionary item to 'diffs' for consistency with other multi-value
items.  Yes, I know it makes the template usage itself weird
... "diffs.changes" instead of "diff.changes", but...

* lib/viewvc.py
  (view_diff): Store the desc.changes in the 'diffs' data dictionary
    item, not the 'diff' item.

* templates/diff.ezt
* templates/include/diff_display.ezt
  Replace uses of 'diff' with 'diffs'.  Selectively, of course.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2643 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-26 19:11:41 +00:00
stilor
6453447c63 * docs/template-authoring-guide.html
(top level) Introduce formatting rules for  4th and 5th levels of variables.
  (diff.ezt) Update for the changes on the property-diff branch. Describe
  missing parts (intraline diffs, patch_href). Fix some typos and copy-paste
  errors.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2642 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-24 20:05:37 +00:00
cmpilato
5e3c53e592 Sync the 'property-diff' branch with outstanding trunk changes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2641 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-21 14:47:51 +00:00
cmpilato
24cbe623e7 * docs/template-authoring-guide.html
Update to refer to ViewVC 1.2, not 1.0.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2640 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-21 14:47:02 +00:00
cmpilato
48a59d30ad * docs/release-notes/1.2.0.html
New stub file.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2639 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-21 14:46:35 +00:00
cmpilato
605e66e56f * docs/release-notes/1.1.0.html
Add some helpful markup comments.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2638 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-21 14:46:15 +00:00
cmpilato
661e7fc569 * notes/releases.txt
Try to document the creation of new release branches (though it's been a
  while since I've done that, so this is all subject to my imperfect memory.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2637 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-21 14:42:33 +00:00
cmpilato
055e1dad53 * docs/upgrading-howto.html
Add stub section for "from 1.1" section.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2635 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-20 19:48:55 +00:00
cmpilato
51f194d4ad * docs/upgrading-howto.html
Dial back the color intensity a bit.  Sheesh.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2634 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-20 19:48:00 +00:00
cmpilato
3f19b50a08 * docs/upgrading-howto.html
Tweak the "from 0.8" section for a bit of consistency with other sections.
  (As if anyone is still running 0.8.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2633 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-20 19:47:14 +00:00
cmpilato
5e22b37a56 Sync the property-diff branch with the latest trunk changes. (Yawn.)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2632 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-19 13:20:57 +00:00
stilor
1e14eccbe0 * diff_display.ezt
(anchor): Replace <a name="xxx"/> with <p id="xxx"/> for XHTML compliance.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2629 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-17 19:13:12 +00:00
stilor
4bf50b31f7 * viewvc.py
Follow existing conventions on indenting function arguments/hash initializers
  carried over to the next line.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2628 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-17 19:06:27 +00:00
stilor
68496ebc9f * diff_display.ezt
* viewvc.py
  Expand tabs to spaces, as per review.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2627 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-17 18:51:52 +00:00
stilor
a789c13698 * viewvc.py
(Request.run_viewvc): Select diff view for directories if r1/r2 arguments
  are provided.
  (view_revision): Generate a link to diff view on directories if there was
  a change in properties.

* log.ezt
  (revision links): Generate 'diff to previous' and 'diff to selected' for
  directories as well as files. Check for URL, not revision when determining
  if 'diff to previous' link should be created (for consistency with 'diff
  to selected')


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2626 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 19:02:32 +00:00
stilor
44a6fe7465 * viewvc.py
(view_diff): Pass anchor locations to the diff template.

* diff_display.ezt
  (top-level): Generate anchors.

* revision.ezt
  (path links): Use anchors to point to content and property parts of the diff.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2625 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 18:50:08 +00:00
stilor
90a1d6cae2 * viewvc.py
(DiffDescription): Add support for property diffs.
  (diff_side_item): Store properties hash in the side items.
  (view_diff): Generate property diffs.

* diff_display.ezt
  (headers): Indicate the property being diffed, if applicable.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2624 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 08:12:51 +00:00
stilor
4fe882972a * viewvc.py:
(view_diff): Remove unused local variables.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2623 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 07:58:18 +00:00
stilor
f57be6e516 * viewvc.py
(DiffDescription): New class. Move the diff format selection and processing
  to this new class. Split the processing into "preparation" and actual diff.

The ideas behind this new class are:
1. It will accumulate the diff "blocks". Currently, it will handle content and
properties. In the future, it can be easily reused to accumulate diffs for
recursive ("revision") diff.
2. It separates the diff method from the selection of content to be diffed.
We have two content inputs (content, properties), four formats (raw, traditional
side-by-side, intraline side-by-side, intraline unified). The former two expect
a stream (fp), the latter two expect array of lines. This class makes each of
these chunks separate from each other.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2622 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 07:29:23 +00:00
stilor
c819b3a3fc * diff_display.ezt
(raw): Gracefully handle the case where there are no changes (output
  "No changes" instead of not displaying anything).

* viewvc.py
  (diff_parse_headers): Detect if there were no input from diff stream,
  report RCSDIFF_NO_CHANGES in this case.
  (view_diff): If binary/empty changes are detected, relay that information
  to the template.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2621 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 07:06:51 +00:00
stilor
682ff6c9b9 * viewvc.py
(diff_side_item): Do not return log_entry, it is not used. Rename
  "bare" and "links" items to more descriptive "prop" and "content".


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2620 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 06:48:47 +00:00
stilor
988f5b535e * viewvc.py
(view_diff): In preparation for factoring out the "differ" code, make view_diff
  always pass the differences as 'changes' array/stream. The format is now selected
  via new 'display_as' argumnet.

* diff_display.ezt
  (all formats): Accommodate changes in viewvc.py



git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2619 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 06:25:38 +00:00
stilor
4780023396 * diff_display.ezt
(all formats): Remove the use of empty <h3> as spacer, instead add margin-top on tables.
  (sidebyside, unified): Specify column widths. Otherwise, files with narrow content
  are displayed with line number columns occupying 50% of the screen.
  (unified): Remove cut-n-paste "width" attribute specification.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2618 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 06:09:42 +00:00
stilor
63227ef2a1 * diff_display.ezt
Remove definitions which have been moved to start of the file.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2617 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 05:57:39 +00:00
stilor
6253fec4fb * diff_display.ezt
(left_header/right_header): Define common left/right side descriptions,
  use them in all diff output formats (adding them to raw and unified, which
  didn't have headers before)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2616 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 05:55:29 +00:00
stilor
7cdab259b8 * viewvc.py
(is_undisplayable): New function to check if property name/value
  can be displayed. Use this function in current file/dir/revision views.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2615 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 05:33:25 +00:00
stilor
d1427e6639 * diff.ezt
(legend): Remove references to revisions (revisions may be the same, e.g.
  if diffing same revision on different branches). There are detailed references
  in the headers.
  (legend): Mention that colors designate not only affected lines, but also
  characters (if intraline diffs are enabled).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2614 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-16 04:48:01 +00:00
stilor
04329a707b * viewvc.py
(view_diff): Make diff an iterable (sequence); later property changes will be
  added to the sequence. Pass hide_legend, left_rev, right_rev outside of the
  diff[] array to untie diff.ezt from include/diff_display.ezt. Rename format
  to diff_format for consistency with template name.

* diff.ezt
  Same, plus small fixes in legend: capitalize first letter in 'changed',
  refer to revision as such, not as 'v.XXX' - such references are not used
  anywhere else.
  


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2613 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-06 05:37:56 +00:00
stilor
888f7ee302 * viewvc.py
(view_diff): Restore try-except block. It is unused for SVN,
  but still used for CVS.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2612 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-06 05:07:49 +00:00
cmpilato
205484b0f2 * INSTALL
Fix pointer to Subversion project website.

Found by:  Daniel Shahaf <d.s@daniel.shahaf.name>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2609 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-04 14:59:08 +00:00
stilor
7466b03e7c * viewvc.py
(view_diff): Factor out common code preparing left/right item information into a new
  function, diff_side_item.
  (diff_side_item): New function.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2607 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-01 19:25:00 +00:00
stilor
737b5d2b23 * viewvc.py
(view_diff): Remove try-except block, exceptions are now handled in
    setup_diff with more specific error messages. Merge the 'if fp'
    block with the preceding conditional - which is the only way fp
    is set to non-None.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2606 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-01 07:10:10 +00:00
stilor
c6775f5b89 * common.py
New file to house common definitions; _item and _RCSDIFF_*
  for now. Remove local definitions in the rest of modules.

* idiff.py
  (sidebyside, unified): Return _RCSDIFF_NO_CHANGES type if
  the generator failed to yield anything else.
  (sidebyside): Add type attribute to the returned tuple so
  that template can check the type.

* diff_display.ezt
  (top-level): Define messages to be used if diffs cannot be
  displayed. Use the definitions in all non-raw displays.
  (sidebyside,unified): Handle _RCSDIFF_IS_BINARY and
  _RCSDIFF_NO_CHANGES; the former is not returned yet - will
  be passed to the template once properties are diffed.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2605 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-01 06:33:22 +00:00
stilor
eba17bcbd7 * viewvc.py
(view_diff): Encapsulate into a single _item parts that would
  be repeated when multiple "diffable" items will be passed to diff.ezt.
  At that time, diff will be changed to a sequence.

* diff_display.ezt, diff.ezt
  Prefix those repeatable parts with 'diff'.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2604 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-01 05:35:15 +00:00
stilor
3e5836cdb4 * diff.ezt
Split the actual diff-formatting part into a separate includable template.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2603 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-01 04:50:35 +00:00
stilor
35544ecbef Create a branch for property diffs integration (issue 383).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/property-diff@2602 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-01 04:26:15 +00:00
cmpilato
a6c9d3ac49 Loop until receiving a definitive answer to the interactive overwrite
query, rather than croaking on an empty response.

* viewvc-install
  (install_file): If raw_input() return an empty string, re-ask the
    question.

Patch by: Alexey Neyman <stilor@att.net>

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2599 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-09-30 19:09:38 +00:00
cmpilato
f44c50a5ed Fix issue #494 ("allow override config from in mod_python"). This
commit allows environmental overrides to trump programmatically
passed-in configuration paths.

* INSTALL
  Mention the VIEWVC_CONF_PATHNAME override environment variable.

* lib/viewvc.py
  (load_config): Document this function and the order in which it
    searches for a configuration path.  Use server.getenv() instead of
    os.environ.get() where we can so that environmental overrides work
    in mod_python.

Patch by: Alon Bar-Lev <alon.barlev@gmail.com>
          (Tweaked by me.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2597 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-09-30 15:45:39 +00:00
cmpilato
b96b8d4003 Revert r2595.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2596 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-09-30 15:15:11 +00:00
cmpilato
814e0d4d6c *** This commit was reverted in r2596 ***
For issue #494 ("allow override config from in mod_python"), use
server.getenv() instead of os.environ.get() to look for the special
VIEWVC_CONF_PATHNAME environment variable.

* INSTALL
  Mention the VIEWVC_CONF_PATHNAME override environment variable.

* lib/viewvc.py
  (load_config): Use server.getenv() instead of os.environ.get().

Patch by: Alon Bar-Lev <alon.barlev@gmail.com>
          (Tweaked by me.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2595 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-09-30 14:59:28 +00:00
cmpilato
1ae87bb8b6 * conf/viewvc.conf.dist
Note the importance of the MIME type maps for syntax coloration, and
  point folks to the 'mime_types_files' option.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2593 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-09-14 17:53:23 +00:00
cmpilato
ec2a0a3f3b Finish issue #491 ("Explain in viewvc.conf.dist the views (used for
allowed_views, etc.)").

* conf/viewvc.conf.dist
  (allowed_views): Add descriptions of the various views.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2591 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-09-07 17:42:59 +00:00
cmpilato
ac75dc9651 * bin/svndbadmin
Fix a minor buglet in the usage message.

Reported by: Jean-Yves Avenard <jyavenard@gmail.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2588 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-07-01 17:57:10 +00:00
cmpilato
59f55fecb6 (More) gracefully handle some errorful input conditions.
* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository._getrev): Re-raise all exceptions as
    InvalidRevision exceptions.

* lib/vclib/svn/svn_repos.py
  (LocalSubversionRepository._getrev): Re-raise all exceptions as
    InvalidRevision exceptions.

Found by:  Daniel Shahaf <d.s@daniel.shahaf.name>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2586 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-06-23 15:25:07 +00:00
cmpilato
7da44377c5 Fix issue #488 ("Allow 'rrNNN' formatting for Subversion revision
specifiers (with multiple 'r')").

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository._getrev): Accept multiple leading 'r's
    in revision specifiers.

* lib/vclib/svn/svn_repos.py
  (LocalSubversionRepository._getrev): Accept multiple leading 'r's
    in revision specifiers.

Patch by:  Daniel Shahaf <d.s@daniel.shahaf.name>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2584 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-06-23 15:09:11 +00:00
cmpilato
9228cc9a89 * CHANGES
Fix a mistaken issue reference.

* INSTALL
  Add a step allowing access to your ViewVC install directory, and delay the
  Apache restart by a step.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2582 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-06-14 13:29:09 +00:00
cmpilato
c355d1ce3e Fix issue #486 ("OperationError raised when trying to do a glob search").
Add code to workaround the fact that in Python 2.6,
fnmatch.translate() stopped returning strings that ended with '$'
(which work fine as MySQL regular expressions) and started instead
returning strings that ended with '\Z(?ms)' (which... don't).

* lib/cvsdb.py
  (CheckinDatabase.SQLQueryListString): If the returned regexp ends
    with '\Z(?ms)', replace those characters with a single '$'

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2580 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-06-06 19:57:51 +00:00
cmpilato
47c12cb194 Fix issue #485 ("Patch view hiding path details").
* lib/viewvc.py
  (diff_parse_headers): Add 'path1' and 'path2' parameters, and use
    those paths in the returned header lines.
  (view_patch, view_diff): Pass paths into diff_parse_headers() now.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2577 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-27 18:58:59 +00:00
cmpilato
c9f9c96db2 * www/index.html
Update latest revision to 1.1.11.

* CHANGES
  Copy 1.1.11's changes into this file.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2574 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-17 12:38:13 +00:00
cmpilato
18a52bd5ce More copyright updates.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2570 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-17 12:17:35 +00:00
cmpilato
02bd4d70db Update some copyright notices.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2569 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-17 12:02:28 +00:00
cmpilato
aac6bbafb9 Add '--help' option to standalone.py.
* bin/standalone.py
  (usage, badusage): New functions.
  (main): Separate the option parsing from the option validation.  Add
    support for a '--help' option.  On bad input, now print a suggestion
    to run the script with '--help' for usage hints.

Patch concept by: Prabhu Gnana Sundar <prabhugs@collab.net>
                  (Heavily, heavily tweaked by me.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2565 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-12 18:34:12 +00:00
cmpilato
90d35a6fc1 * bin/standalone.py
(main): Revert (mostly) a wording change made in r2562.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2563 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-12 13:47:04 +00:00
cmpilato
91995a4241 * bin/standalone.py
(main): Rework the usage message for conciseness, and fix an old typo, too.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2562 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-12 13:39:12 +00:00
cmpilato
eef47fbb1f * bin/standalone.py
(main): Use a pristine Options() object when printing the usage
    message (so the stated default values of options aren't affected
    by what the user provided).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2561 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-12 13:15:32 +00:00
cmpilato
63ddc213ae * bin/standalone.py
(main): Restore alphabetical order of options listed in the usage message.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2560 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-12 13:10:36 +00:00
cmpilato
8eeac0cd59 * bin/standalone.py
(main): Switch to sequence syntax to prevent future headache:
    '-h' in ('--help') returns True, which is not what we expect.
    '-h' in ['--help'] returns False, which is what we want.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2559 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-12 13:09:27 +00:00
cmpilato
5132017538 Fix issue #444 ("ViewVC stumbles over revisionless ,v files when
sticky tag is specified").

* lib/vclib/ccvs/bincvs.py
  (_get_logs): Only try to skip the "rest of the file" if there's
    reason to believe there's a "rest of the file".  While here,
    ensure that the 'tag' variable is defined in all cases (though it
    shouldn't matter).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2557 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-05 14:36:27 +00:00
cmpilato
a00c6ee5cb Fix issue #483 ("Error skipping newphrases when parsing RCS data").
* lib/vclib/ccvs/rcsparse/common.py
  (_Parser.parse_rcs_admin): Chew up newphrases found while parsing
    the admin block.

Patch by: Giovanni Pellicciotta <giovanni.pellicciotta@anubex.com>

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2555 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-21 16:03:31 +00:00
cmpilato
868300daf4 Fix spelling errors introduced in r2551.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2553 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-20 15:01:17 +00:00
cmpilato
61bcf03fcd Fix (to the degree that I believe is reasonable at this time) issue
#433 ("queries return only partial results").  When a database query
is artificially limited by the 'row_limit' setting, inform the user
that the returned data is incomplete.

* lib/cvsdb.py
  (CheckinDatabase.CreateSQLQueryString): Add 'detect_leftover'
    parameter, used internally to check for a reached query limit.
  (CheckinDatabase.RunQuery): Update call to CreateSQLQueryString(),
    and check for leftover query response rows.  If any are found, set
    the appropriate flag on the query object.
  (CheckinDatabaseQuery.__init__): Set initial values for new
    'executed' and 'limit_reached' members.
  (CheckinDatabaseQuery.SetExecuted,
   CheckinDatabaseQuery.SetLimitReached,
   CheckinDatabaseQuery.GetLimitReached,
   CheckinDatabaseQuery.GetCommitList): New functions.

* lib/viewvc.py
  (view_query): Use query.GetCommitList() now instead of poking into
    the query object directly.  Also, check query.GetLimitReached(),
    reporting the findings through the data dictionary (via a new
    'row_limit_reached' item) to the templates.

* lib/query.py
  (run_query): Use query.GetCommitList() now instead of poking into
    the query object directly.  Now return a 2-tuple of commits and a
    limit-reached flag.
  (main): Update expectations of run_query() call.  Populate
    'row_limit_reached' data dictionary item.

* templates/query_results.ezt,
* templates/query.ezt
  Display a warning if the query results are incomplete.

* templates/docroot/styles.css
  (.vc_warning): New style definition.

* docs/template-authoring-guide.html
  Document the new 'row_limit_reached' template item.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2551 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-20 14:50:40 +00:00
cmpilato
1e4c3f92f6 Move a bunch of duplicated statements into a helper function.
While here, add notes to the 'row_limit' and 'rss_row_limit'
configuration option descriptions.

* lib/cvsdb.py
  (CheckinDatabase.GetCommitsTable): New helper.
  (CheckinDatabase.AddCommit, CheckinDatabase.CreateSQLQueryString,
   CheckinDatabase.CheckCommit, CheckinDatabase.sql_delete): Use new
    GetCommitsTable() helper instead of hard-coding the version-specific
    selection of the commits table.

* conf/viewvc.conf.dist
  (row_limit, rss_row_limit): Make it clear what exactly is getting
    limited here.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2550 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-20 14:05:16 +00:00
cmpilato
df71275a13 Try to make some sense of the various CVSdb-related limitation
mechanisms, namely by removing the largely redundant "global" limit
and allowing the per-query row limit (which already exist, too) to do
its work.

While here, remove a poorly conceived (but thankfully unhighlighted)
mechanism for overriding the administrative limit on database rows
which was accessible via URL CGI params.

* lib/viewvc.py
  (_legal_params): Remove 'limit' as a legal parameter.
  (view_query): No longer allow an undocumented URL parameter to
    override the admin-declared SQL row limit.  That should have never
    been allowed!

* lib/cvsdb.py
  (CheckinDatabase.__init__): Remove 'row_limit' parameter and
    associated self._row_limit member.
  (CheckinDatabase.CreateSQLQueryString): No longer fuss with
    self._row_limit.  Let the individual query carry the row limit.
  (ConnectDatabase): Update call to CheckinDatabase().

* lib/query.py
  (form_to_cvsdb_query): Now accept 'cfg' parameter, and set the
    query's row limit from the configured defaults.
  (run_query): Update call to form_to_cvsdb_query().

* docs/url-reference.html
  Remove reference to the 'limit' parameter.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2547 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-19 20:40:04 +00:00
cmpilato
4818a56012 Fix (I think...) issue #479 ("annotate a file, which uses
CVS-keywords").  This changes causes the checkout of CVS file
contents -- when used as part of the markup/annotate logic -- to not
expand keywords.  This helps it to be consistent with the results of
the annotate information query.

* lib/vclib/__init__.py,
* lib/vclib/svn/svn_ra.py,
* lib/vclib/svn/svn_repos.py
* lib/vclib/ccvs/ccvs.py
  (openfile): Add 'options' parameter (unused).  Callers updated.

* lib/vclib/ccvs/bincvs.py
  (openfile): Add 'options' parameter, and look for a 'cvs_oldkeywords'
    option to govern the use of -kkv or -ko in the 'co' command.
    Callers updated.

* lib/viewvc.py
  (markup_or_annotate): Pass 'cvs_oldkeywords' option to repos.openfile().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2545 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-01 16:59:11 +00:00
cmpilato
f12cef277c Fix issue #477 ("Large log messages trigger excessive memory
consumption").

* lib/viewvc.py
  (ViewVCHtmlFormatter._tokenize_text): Switch to a line-based
    approach.  This provides a *vast* improvement in performance and
    memory usage, especially for large log messages with many
    mark-up-able regions.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2543 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-01 14:37:24 +00:00
cmpilato
0ca7fac7fb Finish issue #478 ("Client-facing unhandled exceptions, traceback
dumped") by raising a cleaner error message when asked to display a
"checkout" of a non-file.

* lib/viewvc.py
  (view_checkout): If the target isn't a file, raise a
    ViewVCException().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2541 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-29 16:35:54 +00:00
cmpilato
35a55c317e * lib/vclib/ccvs/blame.py
(CVSParser.extract_revision): Fix a typo (aka "syntax error").

Found by: Cristian Tibirna <tibirna{__AT__}kde.org>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2540 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-28 17:46:28 +00:00
cmpilato
963a3a2094 * bin/standalone.py
(main): Fix broken option handling: -d expected an argument, -c
    didn't and was doubly associated with both --config-file and
    --htpasswd-file.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2538 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-15 20:40:23 +00:00
cmpilato
5ce98d04d6 Bump latest version statement and links, and copy CHANGES from 1.1.10 release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2536 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-15 16:36:35 +00:00
cmpilato
49957ea54b Hopefully, fix issue #475 ("regression: required authorization to
root") and issue #476 ("Stack trace for some users if last commit
deleted a file or folder") by preventing ViewVC from trying to look up
the path type of a deleted path at an invalid location.

* lib/vclib/svn/svn_repos.py
  (LocalSubversionRepository._gettype): New helper function.
  (LocalSubversionRepository.itemtype): Now use _gettype() helper.
  (_get_change_copyinfo): New helper.
  (_simple_auth_check): Rework this function to definitively resolve
    the path type of paths it passes through the authz system,
    handling the complication of determining the path of a now-deleted
    path by consulting its last location prior to deletion.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2531 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-11 20:17:21 +00:00
cmpilato
bd3323410a Add some helpful URLs to the release instructions.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2529 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 20:01:13 +00:00
cmpilato
fce93313e5 Post-release twiddling.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2527 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 19:55:28 +00:00
cmpilato
76dae127d6 Update copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2524 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 19:24:59 +00:00
cmpilato
2f8af5fdb7 * conf/viewvc.conf.dist (log_pagesextra): Set value to match defaults.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2522 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 18:27:58 +00:00
cmpilato
c3dc73d840 Followup to r2517, for issue #457.
* templates/roots.ezt
  Also show the last author, and mark only the "Name" header as the
  sort choice.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2519 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 16:56:16 +00:00
cmpilato
0fe6e33643 Finish issue #457 ("Display repository metadata (last-commit stuff) in
roots view").

* lib/viewvc.py
  (view_roots): Include revision metadata in the data dictionary.
  (list_roots): If configured to do so, calculate some last-modified
    metadata for Subversion roots.

* lib/config.py
  (Config.set_defaults): Set default value for new show_roots_lastmod
    option.

* conf/viewvc.conf.dist
  (show_roots_lastmod): New option.

* templates/roots.ezt
  Show some roots metadata if configured to do so.

* docs/template-authoring-guide.html
  Document new 'roots' view data dictionary items.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2517 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 15:11:01 +00:00
cmpilato
f7df7da7f0 Finish issue #455 ("ViewVC 1.1.x missing URL parsing in file contents
view").

* lib/viewvc.py
  (_re_rewrite_escaped_url): New compiled regexp.
  (markup_escaped_urls): New helper function.
  (markup_stream_pygments): Use markup_escaped_urls() to wrap URLs in
    <a> tags, something Pygments won't do for us.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2514 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-17 16:05:46 +00:00
cmpilato
bc2ab5c093 * lib/vclib/svn/__init__.py
(canonicalize_rootpath): Per the thought found in issue #446
    ("Specifying repo location using file:// prefix in svn_roots leads
    to lack of hyperlinks on revision view"), convert file:/// and
    file://localhost/ URLs into local paths.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2512 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-16 20:27:33 +00:00
cmpilato
2e69660725 Make what should be some decent performance improvements in the
Subversion revision metadata harvesting logic.

* lib/vclib/svn/svn_repos.py
  (LocalSubversionRepository._revinfo): Split this into two logical
    codepaths, one that needs changed-path information along with the
    revision metadata, one which does not.  (Because if we don't need
    it, there are faster ways to get what we *do* need.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2509 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-11 21:33:28 +00:00
cmpilato
51fe3f9d1c * lib/vclib/svn/svn_repos.py
(_get_history, _log_helper): Move these into...
  (LocalSubversionRepository._get_history,
   LocalSubversionRepository._log_helper): ...here.  Update callers.



git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2507 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-11 21:16:13 +00:00
cmpilato
77bf7001a6 * lib/vcauth/svnauthz/__init__.py
(ViewVCAuthorizer._get_paths_for_root): Trap and return a cleaned-up
    error when the authzfile isn't parse-able.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2505 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-11 16:55:11 +00:00
cmpilato
2dfe9c55f0 Clarify that Python 3.x is not yet supported.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2502 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-07 14:47:22 +00:00
cmpilato
c39a091b83 * lib/vclib/svn/svn_ra.py
(client_log): Check for the correct exception.

Reported by: Phil Jeary <Phil.Jeary@dovetailservices.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2500 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-01-04 19:22:32 +00:00
cmpilato
5678e463ad Make the number of "extra pages" of log output configuration. (An old
TODO item I'd forgotten about.)

* lib/config.py
  (Config.set_defaults): Set cfg.options.log_pagesextra default value.

* lib/viewvc.py
  (EXTRA_PAGES): Removed.
  (paging_sws): Add 'extra_pages' parameter, used instead of static
    EXTRA_PAGES variable.
  (view_log): Update call to paging_sws().  Replace uses of static
    EXTRA_PAGES with cfg.options.log_pagesextra.

* conf/viewvc.conf.dist
  (log_pagesextra): New configuration item.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2498 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-09 17:10:58 +00:00
cmpilato
e8ba12d66d Fix issue 425 ("Authorization subsystem needs some optimization").
Give the 'vcauth' subsystem a way to make universal access
determinations, which can seriously improve performance in situations
where a user has universal read access to a repository.

* lib/vcauth/__init__.py
  (GenericViewVCAuthorizer.check_universal_access): New skeletal function.
  (ViewVCAuthorizer.check_universal_access): New function.

* lib/vcauth/svnauthz/__init__.py (ViewVCAuthorizer.check_universal_access),
* lib/vcauth/forbidden/__init__.py (ViewVCAuthorizer.check_universal_access),
* lib/vcauth/forbiddenre/__init__.py (ViewVCAuthorizer.check_universal_access)
  New functions.

* lib/vclib/ccvs/bincvs.py
  (BaseCVSRepository.open): New function.

* lib/vclib/svn/svn_repos.py
  (LocalSubversionRepository.open): Now check for universal read access.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.open): Now check for universal read access.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2496 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-09 16:10:04 +00:00
mhagger
31d16b42fe Provide better error info for invalid CVS timestamps.
Patch by: Craig Leres <leres@tigris.org>
Expanded by: me

The original version of this patch was submitted to cvs2svn issue #141:
http://cvs2svn.tigris.org/issues/show_bug.cgi?id=141

* lib/vclib/ccvs/rcsparse/common.py
  (_Parser._parse_rcs_tree_entry): If there is an error when parsing a
  CVS timestamp, include the revision number in the exception message.
  The calendar.timegm() function is not very picky, but it does emit an
  error if the month number is invalid.



git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2495 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-09 08:37:18 +00:00
cmpilato
31bf260508 Cruft removal. Don't tempt folks to configure the path to their
Subversion client, as ViewVC no longer uses that information.

* lib/vclib/svn/svn_repos.py
  (LocalSubversionRepository.__init__): Don't look for svn client path
    in the 'utilities' dictionary.  We don't use it anyway.

* lib/config.py
  (Config.set_defaults): Don't populate utilities.svn any more.

* conf/viewvc.conf.dist
  (utilities/svn): Remove as unused.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2493 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-06 19:08:20 +00:00
cmpilato
4c965a521d Add some notes about issue #439.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2491 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-03 20:50:43 +00:00
cmpilato
ce4f396604 * lib/vclib/ccvs/bincvs.py
(_path_join): Fix one of my more pathetic coding errors.  (How do
    you lose track of the proper valid variable name in a two-line
    function.  Honestly...)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2490 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-03 17:25:34 +00:00
cmpilato
0c38626f18 * CHANGES
Add 1.1.8 changes.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2489 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-02 21:37:04 +00:00
cmpilato
6ac853c1f4 Update website to reflect latest release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2487 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-02 20:58:13 +00:00
cmpilato
beb2205b64 Minor tweak to the release process notes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2483 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-02 20:37:36 +00:00
cmpilato
060ef83f0e Issue #472 ("Using view=markup on folder causes errors").
* lib/viewvc.py
  (view_markup, view_annotate): Raise a more graceful error message
    when asked for markup or annotate view on a non-file.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2480 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-30 21:11:19 +00:00
cmpilato
de95d36720 * bin/standalone.py
(has_crypt, _check_passwd): If 'crypt' isn't available, look for
    'fcrypt' and use it instead.
  (main): Always show --htpasswd-file option, but raise an informative
    error if someone tries to use it when there's no cryptographic support.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2478 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-30 18:19:28 +00:00
cmpilato
f0e2fa721a * lib/config.py
(Config._is_allowed_section, Config._is_allowed_override): Lose
    unused 'parser' parameter.  Callers updated.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2476 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-30 08:49:20 +00:00
cmpilato
f3629550dd * lib/viewvc.py
(view_directory): Add some more debug timers.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2475 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-24 21:34:16 +00:00
cmpilato
5f50e82bda Fix issue #471 ("standalone.py won't start on Windows: no module named
'crypt'") by making htpasswd file support optional (based on the
availability of the 'crypt' module).

* bin/standalone.py
  (): Conditionally import 'crypt' and define _check_passwd() helper function.
  (ViewVCHTTPRequestHandler.validate_password): Use _check_passwd() now.
  (main): Conditionally built accepted options list and usage string based
    on whether 'crypt' is available.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2473 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-16 16:02:46 +00:00
cmpilato
400eb13d76 Fix issue #467 (I think...) by not claiming a particular content
length when compression would skew that.

* lib/viewvc.py
  (get_writeready_server_file): Add optional 'content_length'
    parameter, and the code to handle it.
  (view_doc): Update call to get_writeready_server_file().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2471 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-10-05 15:01:41 +00:00
cmpilato
c123490436 * CHANGES
Copy in the 1.1.7 changes.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2469 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 18:50:06 +00:00
cmpilato
ce5a303c02 Bump latest release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2467 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 18:47:55 +00:00
cmpilato
40df73e111 Bump some copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2463 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 18:19:03 +00:00
cmpilato
239844c040 * conf/viewvc.conf.dist
(viewvc_base_url): Add another example showing a relative ViewVC
    base URL.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2460 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 15:23:35 +00:00
cmpilato
d6a4d294fb Fix the query script's WSGI and FastCGI deployments.
* lib/sapi.py
  (WsgiServer.FieldStorage): Don't pass self._headers to
    cgi.FieldStorage() -- that parameter is for *input* headers, and
    self._headers is a collection of output headers.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2459 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 14:10:35 +00:00
cmpilato
1d33308f90 Allow users to tell the query script where ViewVC is located while
still generating the same defaults as before.

* conf/viewvc.conf.dist
  (query, query.viewvc_base_url): New section and value.

* lib/config.py
  (_base_sections): Add 'query'.
  (Config.set_defaults): Set default value for cfg.query.viewvc_base_url.

* bin/asp/query.asp,
* bin/cgi/query.cgi,
* bin/mod_python/query.py,
* bin/wsgi/query.fcgi,
* bin/wsgi/query.wsgi,
  Ask the configuration for the location of ViewVC before falling back
  to old defaults.

* templates/query.ezt
  Don't reference unset variables.  Do test the log message for
  empty-ness, dropping a non-breaking space in place where it is.

* templates/include/footer.ezt
  Don't reference unset variables.  

* lib/query.py
  (build_commit): Allow ob.log to be empty.  Don't generate ViewVC
    links if we don't have a base URL for ViewVC.
  (main): Calculate docroot in light of possible absent viewvc_link.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2458 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 13:40:42 +00:00
cmpilato
2012320bfc * lib/vclib/ccvs/ccvs.py
Lose more explicit invocations of 'string' module functions (which
  no worky because we aren't importing that module any more).


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2457 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 11:44:09 +00:00
cmpilato
e2433ea8e9 * lib/viewvc.py
(_re_rewrite_svnrevref): Wrap the regexp with word boundaries.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2455 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-08 18:26:15 +00:00
cmpilato
1d8e32110c * lib/vclib/svn/svn_repos.py
(_split_revprops): Special-case the handling of empty input.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2454 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-08 18:11:28 +00:00
cmpilato
b6f0d36c72 * notes/releases.txt
Tweak the release procedure to include pegging the externals
  definition which pulls in the 'templates-contrib' subdirectory.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2452 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-08 17:03:30 +00:00
cmpilato
42f1c80e74 * CHANGES
The FastCGI support is now coming in 1.1.x.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2448 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-07 19:20:50 +00:00
cmpilato
8360e07a97 Stop importing the 'popen2' module -- we aren't using it.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2446 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-07 19:09:07 +00:00
cmpilato
a0b87889dc * CHANGES: Note recent FastCGI support addition.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2445 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-07 15:50:24 +00:00
cmpilato
8b78e4d0b3 Finish issue #464: Add support for FastCGI (using a WSGI end-around).
* bin/wsgi/viewvc.fcgi,
* bin/wsgi/query.fcgi
  New scripts.

* INSTALL
  Note the additional configuration/deployment option.

* viewvc-install
  Install the .fcgi files, too.

Patch by: Mark A. Ziesemer <ziesemer{_AT_}tigris.org>
          (Tweaked minorly by me.)

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2444 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-07 15:47:53 +00:00
cmpilato
42208db4aa Typo fix. Patch by Mark A. Ziesemer <ziesemer{_AT_}tigris.org>
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2442 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-07 15:41:00 +00:00
cmpilato
ed975ad336 Typo fix. Patch by Mark A. Ziesemer <ziesemer{_AT_}tigris.org>
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2441 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-07 15:40:36 +00:00
cmpilato
6b7c9d066f Finish issue #113: Support auto-markup of revision references.
* lib/viewvc.py
  (_re_rewrite_svnrevref): New regular expression.
  (ViewVCHtmlFormatter.format_svnrevref): New.
  (format_log): Accept 'request' parameter instead of 'cfg' (which can
    be obtained via the request).  For Subversion repositories,
    register a formatter which marks up revision references as links
    to the revision view.
  (): Update callers of format_log().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2439 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-07 15:17:32 +00:00
cmpilato
fb5ca5c1d9 * CHANGES
Note new minimum Python version.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2438 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-03 16:50:33 +00:00
cmpilato
a5df176395 Wow. Drop a "general code cleanup" kind of bomb on the codebase. All
of this is aimed at not paying the maintenance price of supporting
Python versions prior to 2.4 any longer, plus a little bit of just
getting dead code out of the way.

* lib/compat.py
  Remove as unused.

* bin/cvsdbadmin,
* bin/loginfo-handler,
* bin/make-database,
* bin/svndbadmin,
* lib/accept.py,
* lib/blame.py,
* lib/cvsdb.py,
* lib/popen.py,
* lib/query.py,
* lib/sapi.py,
* lib/vcauth/forbidden/__init__.py
* lib/vcauth/forbiddenre/__init__.py,
* lib/vcauth/svnauthz/__init__.py,
* lib/vclib/__init__.py,
* lib/vclib/ccvs/blame.py,
* lib/win32popen.py,
* tests/timelog.py
  Replace explicit import and use of the 'string' module with newer constructs.

* bin/standalone.py,
* lib/viewvc.py 
  No longer use 'compat' module.  Replace explicit import and use of
  the 'string' module with newer constructs.

* lib/dbi.py
  Use calender.timegm() instead of compat.timegm().

* lib/vcauth/__init__.py
  Lose unused module imports.

* lib/config.py,
  Replace explicit import and use of the 'string' module with newer
  constructs where possible.  Lose old ConfigParser patch-up code for
  Python 1.5.1.

* lib/vclib/ccvs/ccvs.py
  Replace explicit import and use of the 'string' module with newer
  constructs where possible.  Import _path_join() from bincvs, and use
  it instead of a bunch of copy-and-pasted string join() statements
  throughout.

* lib/vclib/ccvs/__init__.py
  (cvs_strptime): Moved here from the 'compat' module.

* lib/vclib/ccvs/bincvs.py
  (): No longer use 'compat' module.  Replace explicit import and use
    of the 'string' module with newer constructs.
  (_path_join): New, used now instead of a bunch of copy-and-pasted
    string join() statements throughout.

* viewvc-install
  Don't use the 'compat' module any more.

Also, so some rearranging of non-critical bits.

* misc/:              New directory.
* misc/py2html.py:    Moved from 'lib/py2html.py'.
* misc/PyFontify.py:  Moved from 'lib/PyFontify.py'.
* misc/elemx/:        Moved from 'elemx/'.
* misc/tparse/:       Moved from 'tparse/'.
* tools/make-release
  Omit 'misc' directory from releases, too.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2437 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-03 16:49:52 +00:00
cmpilato
a86bac818e * lib/vclib/svn/svn_repos.py
(BlameSource.__init__): Use client.create_context() instead of
    client.ctx_t() to avoid "NULL proxy object pool" error.

(I should have done this with the changes in r2421, but overlooked it,
I guess.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2435 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-03 16:18:55 +00:00
cmpilato
07ba91e7d9 * lib/vclib/svn/svn_repos.py,
* lib/vclib/svn/svn_ra.py
  Remove explicit import and use of 'string' module.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2434 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-02 19:46:14 +00:00
cmpilato
41bc85c437 * viewvc-install
Purge explicit import and use of the 'string' module.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2433 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-02 19:43:35 +00:00
cmpilato
0e0c657341 * INSTALL
Python 2.4 is now the required Python for trunk.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2432 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-02 19:39:52 +00:00
cmpilato
6b35895704 Parse the "; charset=..." bit out of Subversion's svn:mime-type
property, and use it to inform Pygments as to the file's encoding in
the markup and annotate views (while still providing that information
to the user agent in the checkout view).

* lib/viewvc.py
  (parse_mime_type): New helper function.
  (calculate_mime_type): Now also return the encoding as possibly
    parsed from the svn:mime-type property.  Callers updated.
  (get_writeready_server_file): Add 'encoding' parameter.
  (markup_stream_pygments): Add 'encoding' parameter which, if
    provided, becomes the encoding passed to Pygments.
  (view_checkout): Pass the encoding as/if provided by calculate_mime_type()
    to get_writeready_server_file().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2430 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-08-25 20:09:39 +00:00
cmpilato
3f5c86fe85 ViewVC's options are defined and described to be lower-case, with the
only exception being the freeform [vhost] section values and key-value
file stuffs.  In those places, user-defined case could be helpful, and
is certainly not documented as broken.  So allow it, per issue #466.

* lib/config.py
  (Config.load_config, Config.load_kv_files): Override
    ConfigParser.optionxform() to avoid case normalization of option names.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2428 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-08-23 20:25:58 +00:00
cmpilato
8c2b5cd63a * lib/viewvc.py
(ViewVCHtmlFormatter): Was HtmlFormatter.  Which, uh, has the same
    name as an object imported from Pygments.
  (format_log): Track renamed class.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2426 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-08-19 04:18:56 +00:00
cmpilato
ab1f329994 Empower the revision view for remote Subversion repositories to
display node kind information and, by extension, links to the markup
and diff views for files.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository._revinfo_raw): Tweak _log_cb() inner
    function to make use of the svn_log_changed_path2_t information
    where available.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2424 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-08-13 17:48:31 +00:00
cmpilato
6444e57831 Build the Subversion client context object correctly (thus avoiding a
weird NULL proxy object pool error).

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.open): Use svn_client_create_context()
    instead of svn_client_ctx_t().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2421 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-08-13 16:17:39 +00:00
cmpilato
6e5703686a Note the removal of standalone.py's GUI mode.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2416 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-07-15 21:02:01 +00:00
cmpilato
f548912f9a Finish issue #49 - authentication support for standalone.py.
* bin/standalone.py
  (Options.htpasswd_file): New variable.
  (AuthenticationException): New.
  (ViewVCHTTPRequestHandler.handle_request): Handle AuthenticationException.
  (ViewVCHTTPRequestHandler.validate_password): New.
  (ViewVCHTTPRequestHandler.run_viewvc): If htpasswd authentication is
    enabled, then complain about missing authn creds, and validated
    any presented creds.
  (main): Present and handle the new --htpasswd-file option.  Also, do
    some rudimentary validation of --htpasswd-file and --config-file values.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2414 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-07-15 20:50:54 +00:00
cmpilato
8f9eaf9e26 * bin/standalone.py
(main): Don't accept the -g option.  (This is leftover from a
    previous change.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2413 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-07-15 20:31:17 +00:00
cmpilato
9ce6a651c7 More improvements to standalone.py.
* bin/standalone.py
  (NotViewVCLocationException): New exception.
  (ViewVCHTTPRequestHandler.do_GET, ViewVCHTTPRequestHandler.do_POST):
    Now just thin wrappers around...
  (ViewVCHTTPRequestHandler.handle_request): ...this new function.
  (ViewVCHTTPRequestHandler.is_viewvc): Allow for the case where the
    script alias is empty (a server root deployment).
  (ViewVCHTTPRequestHandler.redirect): Remove as unused (the code's
    been merged into handle_request()).
  (ViewVCHTTPRequestHandler.run_viewvc): Do the is_viewvc() check here
    now.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2411 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-07-15 20:22:36 +00:00
cmpilato
28b4383a7b Remove the GUI mode from standalone.py. Great idea when the tool was
simpler, but I've not done it justice in terms of upkeep.

* bin/standalone.py
  (__author__, __date__, __version__, __credits__): Removed in favor of a
    a simple header comment.
  (Options.start_gui): Removed this member variable.
  (nogui, gui): Removed.
  (main): Was 'cli'.  Remove all option handling for, and usage
    references to, GUI mode.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2409 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-07-15 19:06:09 +00:00
cmpilato
e3042df5a3 * bin/standalone.py
(Options): Minor formatting and noop logic changes.
  (ViewVCHTTPServer): Was ViewVC_Server; move to module scope.
  (ViewVCHTTPRequestHandler): Was ViewVC_Handler; move to module scope.
  (serve): Track class renames.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2408 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-07-15 18:55:23 +00:00
cmpilato
26522f7f71 * bin/standalone.py
Reformat this file for consistency with other bits of ViewVC.  There
  *should* be no meaningful logic changes in this commit.  (If there
  are, it's probably a mistake.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2407 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-07-15 18:43:10 +00:00
cmpilato
ff990c90b5 Record change for previous commit.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2406 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-17 20:51:56 +00:00
cmpilato
5a87b383e9 Finish issue #441: Allow "rNNN" formatting for Subversion revision specifiers.
* lib/vclib/svn/svn_repos.py (LocalSubversionRepository._getrev),
* lib/vclib/svn/svn_ra.py (RemoteSubversionRepository._getrev):
  Now accept revision strings that begin with 'r'.

Patch by: Daniel Shahaf <d.s{__AT__}daniel.shahaf.name>
          (Tweaked by me.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2405 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-17 20:43:28 +00:00
cmpilato
f17e5a76a2 Fix a bug which prevented 'standalone.py -r REPOS' from being used
without a configuration file.

* lib/config.py
  (Config.get_authorizer_and_params_hack): Make sure to always return
    a 2-tuple.  Callers will be expecting that, ya know?

Reported by: Daniel Shahaf <d.s{__AT__}daniel.shahaf.name>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2403 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-17 20:06:59 +00:00
cmpilato
10d460902d Finish issue #453 by teaching ViewVC to display all displayable
Subversion revision properties.

NOTE: The svn_ra logic attempts to do the right thing when
      pre-Subversion 1.5 libraries are install, but I haven't had the
      chance to actually test this out myself.

* templates/revision.ezt
  Include the props.ezt property handler template fragment for use
  with revision props.  Also, minor markup change to a header.

* templates/docroot/styles.css
  Lose some unnecessary stylation.
 
* lib/viewvc.py
  (get_itemprops): Lose unused variable has_binary_props.
  (view_revision): Handle new return value from repos.revinfo(),
    processing extra revision props similarly to the way that file/dir
    props are processed and passing them to the template data
    dictionary.

* lib/vclib/svn/svn_ra.py
  Import _split_revprops() from svn_repos.
  (client_log): New compatibility wrapper.
  (add_log): Reformat as an svn_log_entry_receiver_t callback.
  (RemoteSubversionRepository.dirlogs, _date_from_rev): Track changes
    to self.revinfo().
  (RemoteSubversionRepository.itemlog): Now use client_log().
  (RemoteSubversionRepository._revinfo_raw): Now use client_log() and
    return revprops dict, too.
  (_log_cb): Reformat as an svn_log_entry_receiver_t callback.

* lib/vclib/svn/svn_repos.py
  (_split_revprops): New helper.
  (_log_helper, LocalSubversionRepository.dirlogs, _date_from_rev):
    Track changes to repos._revinfo().
  (_revinfo): Use _split_revprops(), and now return revprops, too.
  (_revinfo_helper): Now return revprops dict, too.
  (_date_from_rev): 

* lib/vclib/__init__.py
  (Repository.revinfo): Update docstring to indicate that this
    interface now returns a revprops dictionary, too.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2401 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-15 15:18:48 +00:00
cmpilato
c6f65cb422 Offer a little more help to folks trying to configure the root_parents
option.

* conf/viewvc.conf.dist
  (root_parents): Rewrite the documentation for this to be more clear
    and to note the valid root type strings.
  
* lib/viewvc.py
  (Request.run_viewvc, expand_root_parents): Make some error strings
    provide more helpful information.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2399 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-03 19:42:33 +00:00
cmpilato
f90d575dd2 Oops. Forgot that direct SVN access requires authn.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2398 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 14:49:01 +00:00
cmpilato
bcb28885a9 i can haz logo?
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2397 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 14:47:43 +00:00
cmpilato
60c2e59707 Trade broken link for working one.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2396 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 14:44:31 +00:00
cmpilato
90e5df36c9 Minor process and verbiage tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2393 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 14:37:39 +00:00
cmpilato
29cba5c197 Remove now-unused Announcements link.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2392 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 14:25:18 +00:00
cmpilato
b60b4148ba Merge in CHANGES from 1.0.12 and 1.1.6.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2391 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 14:20:46 +00:00
cmpilato
0df74987cc Bump latest version.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2390 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 14:19:03 +00:00
cmpilato
bf31982901 Shorten the update/copy instructions.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2381 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 12:50:08 +00:00
cmpilato
bee9ac95be Fix issue #452 - Unable to purge data from query database for
non-existent repository.

* bin/svndbadmin
  (main): Do repository existence checking here, like the corresponding
    code in cvsdbadmin does.  (In fact, structure this function like that
    corresponding cvsdbadmin code.)
  (__main__): No longer do existence checks here.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2379 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-20 19:41:33 +00:00
cmpilato
9e2ee8277b Fix issue #451 - ViewVC RSS feed does not correctly escape contents of
<title>.

* templates/rss.ezt
  Apply the necessary formatting for RSS feed title and description
  elements.  Too bad so many readers have developed their own
  individual ideas about presentation of this under-documented format.

(See also r2376.)

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2377 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-20 19:10:06 +00:00
cmpilato
b07b83d252 Teach the EZT library to handle nested [format] blocks rather than its
current only-use-the-most-recently-noticed formatter approach.

* lib/ezt.py
  (Template._cmd_format): Rename 'printer' parameter to 'formatter'.
  (_print_formatted): New helper function.
  (_write_value): Now pop/restore the whole stack of formatters, and
    use _print_formatted() to do the real output writing.
  (Context.__init__): Rename 'printers' member to 'formatters'.
    Consumers updated.
  (_raw_formatter, _html_formatter, _uri_formatter): Renamed from
    _raw_printer(), _html_printer(), and _uri_printer() respectively.
    Consumers updated.
  (_xml_formatter): New.
  (_formatters): Renamed from _printers.  Consumers updated.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2376 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-20 19:06:11 +00:00
cmpilato
aa3f355ec2 Fix issue #449 - RSS feed returned with no mime type set
* lib/viewvc.py
  (generate_page): Actually pass the content_type to
    get_writeready_server_file().

Patch by: Tom Throckmorton <throck{_AT_}tigris.org>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2374 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-20 15:06:44 +00:00
cmpilato
a04e0a3297 Optimizations for rcsparse.
* lib/vclib/ccvs/rcsparse/default.py
  (_TokenStream): Convert token_term into a frozenset if the Python
    version supports such.
  (_TokenStream.get): Remove unnecessary buffer length calculations.

Patch by: Brian Harring <ferringb{_AT_}tigris.org>

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2372 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-20 14:56:08 +00:00
cmpilato
b922b3604d Fix issue #454 - AttributeError exception trying to escape non-string input.
* lib/sapi.py
  (escape): Ensure that the input is string-ified before doing string
    transformations on it.

Patch by: Michael Henry <drmikehenry{__AT__}tigris.org>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2369 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-06 13:51:30 +00:00
cmpilato
cadb94c8b4 * INSTALL
Fix some formatting, and -- Ooh!  Did you see that?  Why, yes, I
  *did* just pull on some armor-plated underwear in the WSGI
  deployment section.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2367 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-05 15:26:07 +00:00
cmpilato
0d2cad4c2d Lose bogus MIME type in favicon link tag.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2364 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-04-21 19:07:30 +00:00
cmpilato
4a96cb39f5 * lib/viewvc.py
(view_revision): Include a self-referential 'revision_href' item in
    the template data dictionary.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2362 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-04-14 17:16:57 +00:00
cmpilato
ec36a17b1c Bump published latest version number.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2361 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-03-30 15:15:04 +00:00
cmpilato
d899df1771 * lib/vclib/ccvs/rcsparse/__init__.py,
* lib/vclib/ccvs/rcsparse/common.py
  Add some much-needed interface docs and comments.

Patch by: Jon Foster <jon.foster {_AT_} cabot.co.uk>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2360 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-03-29 18:22:04 +00:00
cmpilato
8af6e2d824 Copy CHANGES items from 1.1.5 and 1.0.11 releases.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2359 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-03-29 16:51:11 +00:00
cmpilato
16f6728f48 Duh. Bump copyright year in LICENSE.html file, too.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2352 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-03-29 15:37:05 +00:00
cmpilato
1da4f1a0e3 Copyright bumps only.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2349 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-03-29 15:30:00 +00:00
cmpilato
8af29de901 There were too many ways to do something as simple as HTML escaping in
the ViewVC codebase.  Simplify, conjoin, remove, etc.

* lib/sapi.py
  (escape): New function.  *The* preferred HTML-escaping mechanism.
  (Server.escape): New common Server object escape mechanism (which
    uses the aforementioned escape(), of course).
  (CgiServer.escape, WsgiServer.escape, AspServer.escape,
   ModPythonServer.escape): Lose as unnecessary.

* lib/viewvc.py
  (Request.get_form): Escape hidden form variable names and values.
  (htmlify): Remove.
  (): Replace all uses of cgi.escape() and htmlify() with (directly or
    indirectly) sapi.escape().
  
* lib/query.py
  (main): Use server.escape() instead of cgi.escape().

* lib/blame.py
  (HTMLBlameSource.__getitem__): Use sapi.escape() instead of
    cgi.escape().

* lib/idiff.py
  (_mdiff_split, _differ_split): Use sapi.escape() instead of
    cgi.escape().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2344 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-03-11 19:56:22 +00:00
cmpilato
8018ab4a95 Oops! Fix version typo.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2342 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-03-10 22:01:27 +00:00
cmpilato
eb245f2cde Copy in CHANGES entries for 1.0.10 and 1.1.4 release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2341 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-03-10 21:57:44 +00:00
cmpilato
19cc8e7e7b Bump latest release note.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2335 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-03-10 21:40:11 +00:00
cmpilato
26fa0600c6 Bump copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2330 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-03-10 21:17:28 +00:00
cmpilato
82c5390d06 * lib/viewvc.py
(view_queryform): Escape user-provided input before passing it
    directly off to the templates.  Can you say "XSS attack vector"?


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2326 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-02-10 19:19:53 +00:00
cmpilato
3bfefeaaff * lib/viewvc.py
(query_backup): Switch to using the sapi interface for output file
    I/O instead of using 'print'.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2325 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-02-10 19:18:18 +00:00
cmpilato
3563f77bc0 Fix use of 'allow_tar' in an example immediately after saying that it's been
removed.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2322 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-01-29 14:30:22 +00:00
cmpilato
9dc26d4a01 Fix issue #447: Annotate view broken for local SVN repo.
* lib/vclib/svn/svn_repos.py
  (BlameSource.__init__): Add 'config_dig' parameter, and use it to
    register the Subversion configuration directory.
  (LocalSubversionRepository.__init__): Recognize an empty-string
    config_dir as None.  (Yay for configuration file nuances!)
  (LocalSubversionRepository.annotate): Pass self.config_dir to
    BlameSource().

Patch (mostly) by: Jan Parthey <jpar{_AT_}tigris.org>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2320 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-01-19 15:10:58 +00:00
cmpilato
9fbedaeded * conf/viewvc.conf.dist
Comment tweaks, primarily to note in the authorizer parameter
  sections that those options only apply when 'authorizer' has been
  set to the correct respective authorizer name.

Suggested by: Dan Poirier <poirier{_AT_}pobox.com>

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2318 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-01-08 17:24:47 +00:00
cmpilato
2d0fced231 Add a bit of a workaround -- which fortunately applies to probably 99%
of ViewVC users -- for issue #445 (standalone.py throws exception on
root listing after visiting a root).

* conf/viewvc.conf.dist
  Add warning about using per-root options with standalone.py.

* lib/config.py
  (Config.overlay_root_options): Move the assertion/recording related
    to the one-time-only overlay stuffs down to the point where
    problems might occur.  This gives standalone.py users who don't
    use per-root configuration options the requisite mercy to let
    their servers continue to function.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2316 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-01-07 15:56:29 +00:00
cmpilato
167a98ca4e * INSTALL
Fix a typo.  Found by Arfrever F. T. A. <arfrever.fta{_AT_}gmail.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2315 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-22 21:43:15 +00:00
cmpilato
fa33ac3990 Bump latest version on website.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2314 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-22 20:50:32 +00:00
cmpilato
e5f533a115 Copy in 1.1.3 CHANGES.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2313 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-22 20:50:19 +00:00
cmpilato
bedf47ea58 * 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/trunk@2308 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-08 17:12:44 +00:00
cmpilato
4979fd6091 * conf/viewvc.conf.dist
Try to consistify the way our admonishments appear herein.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2306 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-08 17:05:36 +00:00
cmpilato
94708561eb Move the viewvc.org website directory to the root of the repository.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2303 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-04 21:49:18 +00:00
cmpilato
050eb3c433 Add some screenshots, and make some minor style tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2302 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-04 21:48:26 +00:00
cmpilato
2d091dc582 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.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2300 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-03 06:04:32 +00:00
cmpilato
fe6b7afb66 * lib/query.py
(run_query): Expand roots so we're playing with a full deck.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2297 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-03 05:57:22 +00:00
cmpilato
a6565f89f9 Appease the monster in me who growls every time he finds a website
whose primary navigation links aren't preserved on every page.

* viewvc.org/who.html,
* viewvc.org/faq.html,
* viewvc.org/download.html,
* viewvc.org/contact.html
  Lose the 'Project Page' and 'Contributing' nav links.

* viewvc.org/index.html
  Lose the 'Project Page' and 'Contributing' nav links.  Put the
  (short) license on this page.

* viewvc.org/contributing.html
  Lose the 'Project Page' and 'Contributing' nav links.  Put info
  about the viewvc.tigris.org site on this page.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2295 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-02 19:49:25 +00:00
cmpilato
59e6fa414e 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>

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2293 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-02 16:01:49 +00:00
cmpilato
eda31f6050 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).

* conf/viewvc.conf.dist
  Add comments about the scope and permissability of per-vhost and
  per-root overrides.

* lib/config.py
  (): Add some comments about option layering -- including a fancy
    schmancy ASCII art diagram! -- and make some minor comment
    formatting tweaks.
  (Config._base_sections): Was Config._sections.  Add 'authz-*'.
  (Config._force_multi_value): Reformat and comment.
  (Config._allowed_overrides): New mapping of valid overrides.
  (Config.__init__): Track renamed _base_sections member, and avoid
    instantiating _sub_config items for non-explicitly-name base
    sections found therein.
  (Config.load_config): Add docstring.  Use _is_allowed_section() to
    determine which sections to process.
  (Config.load_kv_files): Add docstring.
  (Config.path): Minor docstring tweak. 
  (Config._process_section): Instantiate missing _sub_config() items.
    Add a FIXME comment.
  (Config._is_allowed_section, Config._is_allowed_override): New
    functions.
  (Config._process_vhost): Now use _is_allowed_override() for section
    filtering.
  (Config._process_root_options): Removed, merged with tweaks into...
  (Config.overlay_root_options): ...here, which now uses
    _is_allowed_override for section filtering.
  (Config.get_authorizer_params): Look in the calculated configuration
    rather than specific configuration file regions.  Add a FIXME
    comment.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2292 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-01 20:16:17 +00:00
cmpilato
438eb061bb * lib/config.py
(Config.load_config): Remove 'rootname' parameter that's unused by
    callers, and the code that employs it within this function.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2291 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-01 17:36:41 +00:00
cmpilato
3a4ca4d18a * 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/trunk@2288 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-01 14:58:32 +00:00
cmpilato
d1cbb81fb5 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().

Tested by: Jon Foster <jonfoster{_AT_}tigris.org>

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2286 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-12-01 14:53:10 +00:00
cmpilato
cd62600043 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/trunk@2281 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-11-10 22:16:17 +00:00
cmpilato
7e3a9d7f4d 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>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2279 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-11-05 16:35:05 +00:00
cmpilato
11efd97b23 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>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2277 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-10-25 22:06:54 +00:00
cmpilato
54086fd651 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.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2275 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-10-25 20:46:09 +00:00
cmpilato
1f8c5aca37 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/trunk@2273 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-10-08 17:53:14 +00:00
cmpilato
f182d12e97 As part of issue #397, add initial support for mod_wsgi deployments.
* lib/sapi.py
  (WsgiServer): New class.

* bin/wsgi,
* bin/wsgi/viewvc.wsgi,
* bin/wsgi/query.wsgi
  New stubs.

* viewvc-install
  (FILE_INFO_LIST): Also install WSGI scripts.

* INSTALL
  Add instructions for WSGI deployment.

Patch (mostly) by: Rune Halvorsen <runefh{_AT_}gmail.com>

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2271 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-10-08 14:56:25 +00:00
cmpilato
4c9aa8e1a6 Minor tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2270 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-10-08 14:50:04 +00:00
cmpilato
6e4710d1c6 Clarify the Apache configuration instructions.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2269 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-10-08 14:48:02 +00:00
cmpilato
98f1b93068 * lib/vclib/svn/svn_repos.py
(LocalSubversionRepository.open): Remove custom SIGTERM handler.
    It's causing problems for mod_python deployments, and I can't seem
    to make it do anything meaningful for CGI deployments either.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2268 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-10-06 19:11:28 +00:00
cmpilato
9015d12885 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>

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2265 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-09-09 18:54:15 +00:00
cmpilato
0f376ac9a4 [singing] A spoonful of proofreading helps the temperature go down...
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2263 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-09-09 16:03:02 +00:00
cmpilato
761a97cb47 * 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/trunk@2261 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-09-09 15:57:43 +00:00
cmpilato
f72e67fa3e * templates/file.ezt
Don't point folks to the checkout view if it's disabled.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2259 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-09-09 14:18:15 +00:00
cmpilato
0b42d41e43 * 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/trunk@2257 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-31 14:54:26 +00:00
cmpilato
e7a9e33a36 Make some tweaks to the HtmlFormatter to allow more complex usages.
* lib/viewvc.py
  (HtmlFormatter): Add docstring.
  (HtmlFormatter.get_result): Now return the total number of readable
    characters, too.
  (format_log): Update expected return values from lf.get_result().  


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2255 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-27 15:17:37 +00:00
cmpilato
8a8e8cd1f5 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.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2252 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-25 15:25:16 +00:00
cmpilato
40581a211c * 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.

Reported by: Lawrence Cutlip-Mason <rena.larry.cm{_AT_}gmail.com>

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2251 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-25 15:22:04 +00:00
cmpilato
6b4297e7f5 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.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2249 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-24 15:08:58 +00:00
cmpilato
884a5de16d 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/trunk@2247 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-21 15:58:19 +00:00
cmpilato
589f111f20 Follow-up to r2244 with some changes that allow you to register
userdata along with the log message formatters for use by the
conversion functions.  While ViewVC doesn't use this right now, the
feature has proven useful in writing an add-on that allows somewhat
generic highlighting of issue/bug IDs to URLs.

* lib/viewvc.py
  (HtmlFormatToken): Remove.
  (HtmlFormatter.format_url, HtmlFormatter.format_email_obfuscated,
   HtmlFormatter.format_email_truncated, HtmlFormatter.format_email,
   HtmlFormatter.format_text): Now accept (unused) 'userdata' parameter.
  (HtmlFormatter.add_formatter): Now accept 'userdata' parameter, associated
    with the formatter for future use.
  (HtmlFormatter.get_result): Pass the 'userdata' from the token to its
    converter.
  (HtmlFormatter._tokenize_text): Switch to using _item()s instead of
    HtmlFormatTokens.  Associate with each item the userdata for its converter.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2245 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-21 15:05:21 +00:00
cmpilato
a2cd81f304 Begin taking steps toward customizable log message markup/formatting,
and I think, actually fix ancient issue #3 ("URL cut off in elipsis")!

Incidentally, this change also makes the email address obfuscation
stuff work as documented.  Not sure how I started munging data when
mangle_email_addresses=1 (by substituting " at " for the at-sign), but
no more.

* lib/viewvc.py
  (htmlify): Drop 'mangle_email_addresses' parameter, which was mostly
    unused, and no longer do anything except very basic HTML encoding.
    Callers updated.
  (_re_rewrite_url): Tweak to match *non*-HTML-encoded URLs now.
  (HtmlFormatToken, HtmlFormatter): New classes for log formatting.
  (mangle_email_addresses): Remove as unused.
  (format_log): Add 'maxlen' parameter, allowing this function to be used
    for both long- and short-form log message formatting.
  (copy_stream): Drop unnecessary 'cfg' parameter.  Update call to htmlify().
  (MarkupPipeWrapper.__init__): Drop 'cfg' parameter as unused.
  (MarkupPipeWrapper.__call__): Update call to htmlify().
  (markup_or_annotate, view_revision, view_directory, view_log,
    build_commit): Use format_log() to format both long- and
    short-form log messages.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2244 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-20 20:38:32 +00:00
cmpilato
eb69c40687 Merge in 1.0.9 and 1.1.2's changes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2242 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-11 16:00:21 +00:00
cmpilato
6cbb3fadac Note new latest release on project homepage.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2241 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-11 15:58:09 +00:00
cmpilato
cc4589ab10 Rename 'Source tarballs' left-nav as 'Downloads'.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2240 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-11 15:26:33 +00:00
cmpilato
fcff592db3 Rename 'Subversion' left-nav as 'Source Code'.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2239 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-11 15:25:55 +00:00
cmpilato
694a14e0d2 Update 'Mailing Lists' left-nav link.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2238 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-08-11 15:25:14 +00:00
cmpilato
205b3b67e1 Make it easier for folks to show/hide binary file contents in the
markup/annotate views.

* templates/file.ezt
  Add some customizable template logic to hide the contents of file's with
  non-human-readable file formats.  Were Greg Stein dead, he'd be rolling
  over in his grave right now.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2228 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-16 19:28:29 +00:00
cmpilato
8df8e878f4 * lib/viewvc.py
(view_error): Settle for simple HTML-escaping of error messages
    rather than full htmlification.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2225 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-14 20:43:46 +00:00
cmpilato
f038efef96 Fix issue #422: syntax coloration dropping initial blank lines from
files.

* lib/viewvc.py
  (markup_stream_pygments): Pass stripnl=False to Pygments lexers so
    they won't strip out leading blank lines from the files they are
    colorizing.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2223 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-13 17:13:16 +00:00
cmpilato
52584dec66 Document some shortcomings of the path-based authz subsystem.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2222 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-13 14:09:17 +00:00
cmpilato
5c9f504727 * conf/viewvc.conf.dist
(allow_compress): Add a note about the speed penalty this option causes.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2220 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-07 14:05:12 +00:00
cmpilato
7817608915 Try to avoid content spoofing errors. Prior to this change, a URL
like this:

   http://localhost/viewvc/subversion/?view=foo%22)%20was%20passed\
   %20as%20a%20parameter.20%20Visit%20http://www.baddy.com%20to%20\
   figure%20out%20why%20(%22foo

Would result in an error page that read as follows:

   An illegal value ("foo") was passed as a parameter. Visit
   http://www.baddy.com to figure out why ("foo") was passed as a
   parameter.

(where "http://www.baddy.com" was linkified, and could potentially
point to a malicious website.)

With this change, we will avoid printing unknown parameter names, and
will show the parameter name rather its value when a bogus value is
detected.  Yes, there's admittedly some information loss here that
might be useful to the well-meaning but fat-fingered user, but the
security aspect is more important.

* lib/viewvc.py
  (_validate_param): Don't print illegal parameter names, and show the
    (legal) parameter name rather than its value when that value is
    illegal.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2217 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-06 19:03:06 +00:00
cmpilato
0292a54ec0 Some more around input validation, cleaning up some code and
tightening up some other bits.

* lib/viewvc.py
  (_validate_view, _validate_mimetype): New validation functions.
  (_re_validate_mimetype): Removed as unused.
  (_legal_params): Tweak the validation routines for 'view' and
    'content-type' to point to _validate_view() and _validate_mimetype(),
    respectively.
  (_validate_param): Now check for non-zero result from validation
    functions as the "valid" indicator, rather than requiring each of
    them to raise an exception in troubled times.
  (Request.run_viewvc): Lose now-unnecessary validation of
    'content-type' parameter -- the normal parameter validation stuff
    should catch problems there.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2213 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-06 15:05:23 +00:00
cmpilato
6c93d8c8d1 * lib/viewvc.py
(Request.run_viewvc): Move the logic that maps view=rev to
    view=revision into the parameter validation loop.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2212 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-06 14:48:53 +00:00
cmpilato
4f622d375b * lib/viewvc.py
(Request.run_viewvc): Use an intermediate variable to simplify some
    logic around input parameter validation.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2211 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-07-06 14:46:21 +00:00
cmpilato
867514f9ef Minor comment tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2209 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-17 21:22:14 +00:00
cmpilato
b36d8584e8 * conf/viewvc.conf.dist
Comment out all the options here, forcing folks to uncomment the
  stuff they want to modify.  This allows the parsing of this file to
  be optimized toward doing less work, while maintaining a consistent
  initial presentation.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2207 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-17 14:32:16 +00:00
cmpilato
8e848a4025 Finish issue #415 - ModPythonServer shouldn't call sys.exit().
(This doesn't really fix anything, and hopefully doesn't break anything
either, but is merely a "good programming practice" cleanup item.)

* lib/viewvc.py
  (Request.run_viewvc): Ensure that server.redirect() is the last
    thing this function does (when it does it).

* lib/sapi.py
  (CgiServer.redirect): Avoid calling sys.exit().  Also, fix a use of
    print() that should be sys.stdout.write() (for consistency).
  (AspServer.redirect, ModPythonServer.redirect): Avoid calling sys.exit().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2205 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-16 15:04:24 +00:00
cmpilato
e7e8beca0a Finish issue #336 - Custom cvsgraph rendering via CGI parameters.
* conf/viewvc.conf.dist
  (allowed_cvsgraph_useropts): New option.

* lib/config.py
  (Config.set_defaults): Set default value for new
    'allowed_cvsgraph_useropts' option.

* lib/viewvc.py
  (_legal_params): Add new legal parameters: 'gflip', 'gbbox',
    'gshow', 'gleft', and 'gmaxtag'.
  (cvsgraph_make_reqopt, cvsgraph_normalize_gshow, cvsgraph_extraopts):
    New helper functions.
  (view_cvsgraph_image): Now pass the -O option set to the cvsgraph
    binary.
  (view_cvsgraph): Now pass the -O option set to the cvsgraph binary.
    Also, add new template data stuff for generating a user-configurable
    graph display options form.

* templates/graph.ezt,
* templates/docroot/styles.css
  Add template stuff for generating a user-configurable graph display
  options form.

* docs/url-reference.html,
* docs/template-authoring-guide.html
  Update documentation.

* CHANGES
  Note this change.

Patch by:  Bertho Stultiens <bertho{_AT_}j.auh.dk> (originally),
           Jene Jasper <tigris{_AT_}shadowland.demon.nl> (port to 1.1.x),
           me (tweaks for official ViewVC inclusion)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2204 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-15 20:18:35 +00:00
cmpilato
c3bdeea60b Correct some inconsistent markup in documentation.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2201 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-15 20:11:33 +00:00
cmpilato
3d8aecfd95 Tighten up some input validation.
* lib/viewvc.py
  (_re_validate_boolint): New.
  (_legal_params): Switch the validation function to _re_validate_boolint
   for the following: 'hideattic', 'makeimage', 'parent', 'tarball',
   and 'hidecvsroot'.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2199 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-15 19:41:30 +00:00
cmpilato
c713952d5e Minor documentation wording tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2197 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-15 19:06:26 +00:00
cmpilato
8910c295cc Finish issue #420 - "cvsdbadmin rebuild" and "svndbadmin rebuild"
complain about unknown repositories.

* bin/svndbadmin
  (main): When rebuilding, ignore the UnknownRepositoryError; when
    purging, turn that sucker into something readable.

* bin/cvsdbadmin
  (__main__): When rebuilding, ignore the UnknownRepositoryError; when
    purging, turn that sucker into something readable.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2195 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-08 15:50:51 +00:00
cmpilato
1d496a8f51 Make error handling in cvsdb a little more flexible.
* lib/cvsdb.py
  (UnknownRepositoryError, DatabaseVersionError): New Exception classes.
  (CheckinDatabase.Connect): Raise DatabaseVersionError instead of a
    generic Exception.
  (CheckinDatabase.PurgeRepository): Raise UnknownRepositoryError
    instead of a generic Exception.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2194 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-08 15:23:16 +00:00
cmpilato
892a951493 Finish issue #419: svnauthz module doesn't match Subversion's
case-handling semantics.

* conf/viewvc.conf.dist
  (force_username_case): New option.

* lib/vcauth/svnauthz/__init__.py
  (ViewVCAuthorizer.__init__): Find and handle new force_username_case
    authorizer option.
  (ViewVCAuthorizer._get_paths_for_root): Replace ConfigParser.optionxform()
    (which does normalization of option names) with an identity function
    of sorts.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2192 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-05 19:03:07 +00:00
cmpilato
1371a71f20 Consistify the layout -- at least on a section-by-section basis -- of
the viewvc.conf.dist file.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2190 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-05 17:52:47 +00:00
cmpilato
aba3033c27 Remove CHANGES item for change that should appear in 1.1.2 instead of 1.2.0.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2189 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-04 15:43:55 +00:00
cmpilato
74bfd3e9d8 Post-release fun.
* CHANGES
  Copy 1.1.1's changes here.

* www/index.html
  Update 'latest release' notes.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2186 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-06-03 15:05:32 +00:00
cmpilato
7cb54076fe Finish (for now) the 1.1.0 release ntoes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2183 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-29 18:16:41 +00:00
cmpilato
197f352804 * templates/file.ezt
Use "Contents of /path/to/file" instead of "Annotate of /path/to/file" as
  the page title.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2181 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-29 17:57:03 +00:00
cmpilato
f48559766a * lib/cvsdb.py
(CheckinDatabase.PurgeRepository): Don't allow the purge operation
    to actually *create* a "repositories" table entry.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2179 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-29 15:09:51 +00:00
cmpilato
546501816b Fix issue #417 - "cvsdbadmin rebuild" fails to record some of the
commit/repository info.

* lib/cvsdb.py
  (CheckinDatabase.PurgeRepository): Clear all the ID caches after
    running a purge operation so that, if the purge is actually part
    of a rebuild, the subsequent update doesn't merely pull IDs from
    the cache (with the side-effect of not readding them to the database).

Reported by: Naran Babhu <naranbabhu{_AT_}tigris.org
             Martin Dessureault <martin{_AT_}austin.rr.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2178 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-29 15:08:32 +00:00
cmpilato
8089335fc6 Fix issue #416 - Can't see query form due to missing template variables.
* lib/viewvc.py
  (view_queryform): Add 'query_action' and 'query_hidden_values' back
    to the data dictionary, as they were accidentally dropped in r2123.

Reported by: Naran Babhu <naranbabhu{_AT_}tigris.org>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2175 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-28 13:59:47 +00:00
cmpilato
d739127ea4 Update external to point to 1.2 versions of contributed templates.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2172 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-18 13:28:53 +00:00
cmpilato
4b4f450ecb Update CHANGES list for 1.1.0 release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2169 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 17:11:27 +00:00
cmpilato
588f9000e9 Update the viewvc.org site.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2168 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:44:58 +00:00
cmpilato
7ecae1790a One last tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2167 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:43:13 +00:00
cmpilato
a154179492 Need ... homepage ... beauty...
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2166 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:41:52 +00:00
cmpilato
cc4f8254d6 More Tigris homepage tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2165 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:40:24 +00:00
cmpilato
c695ede476 Update Tigris homepage.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2164 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:31:11 +00:00
cmpilato
c910df0835 Add left-nav link to source tarballs.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2163 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-13 14:20:53 +00:00
cmpilato
914e0e7521 Copy 1.0.8's CHANGES.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2152 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 17:26:04 +00:00
cmpilato
4c81b1fd91 Note new version of ViewVC.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2151 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 17:25:04 +00:00
cmpilato
92a95601d1 Bump copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2146 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 17:08:54 +00:00
cmpilato
211bc90343 Finish issue #402: Split the 'use_pagesize' configuration directive
into two: 'log_pagesize' and 'dir_pagesize', individually controlling
the size of pages used (if at all) for the revision log and directory
views, respectively.

* conf/viewvc.conf.dist
  (use_pagesize): Removed, in favor of...
  (dir_pagesize, log_pagesize): ...these.

* lib/viewvc.py,
* lib/config.py,
* bin/standalone.py
  Replace the use and tooling around 'use_pagesize' with
  'dir_pagesize' and 'log_pagesize' as appropriate.

* templates/include/paging.ezt,
* templates/include/dir_header.ezt
  Stop using the option value to determine whether or not to show
  pagination UI.  Use the presence of some pages instead.
  
* docs/upgrading-howto.html
  Note the changes in options.

Patch by: Lei Zhang <thestig{_AT_}google.com>
          (Tweaked by me.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2142 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 15:45:25 +00:00
cmpilato
fdff86c5f4 Fix the GUI mode of standalone.py -- it wasn't even starting
correctly, and we don't need a toggle for 'enscript' any more.

* bin/standalone.py
  (GUI.__init__): Pass the configuration file to handle_config().
    Lose 'enscript'-related stuff.
  (GUI.toggle_use_enscript): Remove.
  (cli): Fix call to gui(), dropping bogus parameter.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2140 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 15:34:11 +00:00
cmpilato
7ca7528869 Fix issue #409, an exception thrown when sorting by revision in a
remote Subversion directory view.

* lib/vclib/svn/svn_ra.py
  (RemoteSubversionRepository.dirlogs): Store the entry's revision as
    a string, for consistency with other vclib modules.

Reported by: Wojciech Wróblewski <wojtek{_AT_}elmi.pl>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2137 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-05 14:52:35 +00:00
cmpilato
dfa98b490f Fix an exception in log views of Subversion repositories with 0
revisions.

* lib/vclib/svn/svn_repos.py
  (_get_history): Add easy-out for repositories with 0 revisions.

Reported by: Wojciech Wróblewski <wojtek{_AT_}elmi.pl>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2134 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-05-04 14:53:24 +00:00
cmpilato
bacb71b1d2 Call make-release correctly.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2132 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-04-20 17:47:50 +00:00
cmpilato
3da11f485a Slight terminology tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2129 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-04-20 17:40:51 +00:00
cmpilato
a8e6f976ef * tools/make-release
Make this able to build any branch, not just trunk and tag names.

* notes/releases.txt
  Update example of using make-release.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2128 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-04-20 17:38:57 +00:00
cmpilato
6d6a0287ac * lib/viewvc.py
(view_directory, view_log): Add some comments explaining the
    presence of certain data dictionary dummy values.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2124 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-24 17:01:53 +00:00
cmpilato
63af297920 Try to make ViewVC provide a consistent, predictable data dictionary
in all of its templated views.  Do this with a custom, dictionary-like
class that prevents the creation of new keys after an initial
instantiation of keys and (possibly dummy) values.

* lib/ezt.py
  (TemplateData): New.

* lib/viewvc.py
  (common_template_data): Now return an ezt.TemplateData() object.
  (make_comma_sep_list_string): New.
  (markup_or_annotate): Use new ezt.TemplateData() interface now.  Use
    make_comma_sep_list_string() where applicable.
  (view_roots, view_log, view_cvsgraph, view_diff, view_revision,
   view_queryform, view_query): Use new ezt.TemplateData() interface now.
  (view_directory): Use new ezt.TemplateData() interface now.  Drop
    'search_re_form' dictionary item.

* lib/query.py
  (main): Use new ezt.TemplateData() interface now.  While here, drop
    'script_name' calculation (it isn't used).

* templates/include/dir_header.ezt
  Display the search form if 'search_re_action' is set, not 'search_re_form'.

* docs/template-authoring-guide.html
  Remove 'search_re_form'.  Update 'search_re_action' description.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2123 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-24 16:55:25 +00:00
cmpilato
d7ac8ee2d4 * templates/include/file_header.ezt
Lose template reference of 'log_href_rev', which was removed in
  r2116 as unused.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2121 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-24 16:32:29 +00:00
cmpilato
11b4c4f4f8 Clutterkiller! Put the default configuration files into a conf/
subdirectory.

* conf/
  New subdirectory.

* cvsgraph.conf.dist,
* mimetypes.conf.dist,
* viewvc.conf.dist
  Move these from here ...

* conf/cvsgraph.conf.dist,
* conf/mimetypes.conf.dist,
* conf/viewvc.conf.dist
  ... to here.

* viewvc-install
  (FILE_INFO_LIST): Track new locations of configuration files.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2119 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 20:41:41 +00:00
cmpilato
c6671ecc50 Attempt to compensate for the fact that some versions of Subversion's
Python bindings do not correctly set the .apr_err and .message members
of SubversionException objects by patching up those exceptions using
the exception arguments.

(Unfortunately, I don't have a version of these bindings installed for
testing.)

* lib/vclib/svn/svn_repos.py
  (_fix_subversion_exception): New helper function.
  (_get_history, BlameSource.__init__, LocalSubversionRepository.rawdiff,
   LocalSubversionRepository.get_location, LocalSubversionRepository.last_rev):
    Use _fix_subversion_exception().

* lib/vclib/svn/svn_ra.py
  (): Import _fix_subversion_exception from svn_repos.
  (RemoteSubversionRepository._date_from_rev, 
   RemoteSubversionRepository.get_location): Use _fix_subversion_exception().


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2117 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 18:02:35 +00:00
cmpilato
0bcb42c158 * lib/viewvc.py
(common_template_data): Stop initializing an unused value.

* docs/template-authoring-guide.html
  Lose docs for unused (and spelled wrong, to boot) template variable.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2116 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 17:46:54 +00:00
cmpilato
586c6bfb9e * lib/ezt.py
(Template._cmd_for): Raise a more useful exception message when the
    to-be-looped-over variable ain't a sequence.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2115 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 17:44:07 +00:00
cmpilato
ca12b5257c * lib/ezt.py
(Template._cmd_print): Raise a more helpful exception to give some
    clues to template authors who tried to, say, print a sequence
    reference.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2111 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-19 15:21:30 +00:00
cmpilato
7fd9e405a1 Update copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2107 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-18 16:43:33 +00:00
cmpilato
6361a849a6 Update release notes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2106 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-18 16:15:49 +00:00
cmpilato
e72184e288 Fix namespace problem with raised Exception.
* lib/viewvc.py
  (view_revision): raise *debug.*ViewVCException.

Patch by: Kamesh Jayachandran <kamesh@collab.net>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2103 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-18 12:45:47 +00:00
cmpilato
2dc51276ce Finish issue #401: Support MIME type overrides in ViewVC configuration.
Trade the 'mime_types_file' option for 'mime_types_files' -- an
ordered list of MIME mapping files to consult.  And provide our own
(empty) mapping file that folks can use to override the mappings
provided by other such files.

* mimetypes.conf.dist
  New file.

* viewvc.conf.dist
  (mime_types_files): Was mime_types_file, and now accepts multiple values.

* lib/config.py
  (Config._force_multi_value): Add "mime_types_files" to the list of
    multi-value configuration options.
  (Config.set_defaults): Track rename of mime_types_file parameter,
    now setting the default to a list containing only "mimetypes.conf".

* lib/viewvc.py
  (load_config): Track new name and format of mime_types_files option.

* viewvc-install
  (FILE_INFO_LIST): Also install mimetypes.conf.dist as itself and as
    mimetypes.conf.

* INSTALL
  (INSTALLING VIEWVC): Update reference to renamed configuration option.

* docs/upgrading-howto.html
  Update this document.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2101 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-03-06 16:43:04 +00:00
cmpilato
e1575692be Finish issue #396 - Malformed accept-language header crashes viewvc
* lib/accept.py
  (AcceptLanguageParseError): Was AcceptParseError.
  (_parse): Track renamed Exception.

* lib/viewvc.py
  (Request.__init__): Catch and handle raised AcceptLanguageParseError.

Patch by: Rune Halvorsen <runefh{_AT_}gmail.com>,
          me


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2097 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-26 16:12:15 +00:00
cmpilato
748efe3815 Add FAQ entry about exposing ViewVC at a vhost root.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2096 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-24 13:36:41 +00:00
cmpilato
d681df85d3 Allow ViewVC to (optionally) use the 'chardet' module during syntax
coloration to perform character encoding detection (and subsequent
translation to UTF-8).

(Thanks to Jeremy Whitlock <jcscoobyrs@gmail.com> for pointing me in
this direction, if inadvertantly.)

* viewvc.conf.dist
  (options.detect_encoding): New.

* lib/config.py
  (Config.set_defaults): Initialize the 'detect_encoding' parameter.

* lib/viewvc.py
  (markup_stream_pygments): If the configuration asks for character
    encoding detection, try to import the 'chardet' module and -- if all
    goes well -- tell Pygments to use it.

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2095 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-20 17:23:27 +00:00
cmpilato
e53b3af42f ViewVC doesn't "do" directory entry sorting by revision for CVS, so
don't imply that it does in the UI.

* lib/viewvc.py
  (view_directory): Don't provide sortby_rev_href to the template.

* templates/directory.ezt
  Only offer sort links when those links are provided by ViewVC.

* templates/dir_new.ezt
  Only offer sort links when those links are provided by ViewVC.  Use
  date-based sorting if rev-based sorting isn't available.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2090 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-20 15:20:15 +00:00
cmpilato
d1a7412c6d Remove dead code.
* lib/popen.py
  (pipe_cmds, _copy): Remove as unused.

* bin/standalone.py
  (StandaloneServer.run_viewvc): Tweak comment that referred to pipe_cmds().

Found by: Rune Halvorsen <runeh {AT} sanedefaults.net>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2088 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-03 03:53:25 +00:00
cmpilato
ee692db2b0 Fix issue #398 (File Log Viewer Shows Wrong Log Message) by asking the
vclib layer for revision-sorted output rather than "whatever you want
to give me".

* lib/viewvc.py
  (markup_or_annotate): Pass vclib.SORTBY_REV instead of vclib.SORTBY_DEFAULT
    to the itemlog() interface.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2086 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-02-02 18:10:59 +00:00
cmpilato
544ca74ed9 Add a skeletal version of some semblance of 1.1.0 release notes, taken
from an email response I gave on the dev@ list.  Will massage this
into something a little more ready for publicity later.

* docs/release-notes,
* docs/release-notes/1.1.0.html
  New.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2085 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-15 16:56:44 +00:00
cmpilato
f6844dda91 Finish issue #334 by adding an empty CSS class def and some references
to it for line number columns in the diff view.

* templates/docroot/styles.css
  (.vc_diff_line_number): New.

* templates/diff.ezt
  Use new vc_diff_line_number class.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2083 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 19:10:09 +00:00
cmpilato
5fdfefa137 Document the new database schema stuff.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2081 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 18:44:43 +00:00
cmpilato
cd64b5da8b Add some real command-line parameter handling to 'make-database',
effectively allowing for non-interactive operation.

* bin/make-database
  (INTRO_TEXT): Removed, merged into the usage message printed by...
  (usage_and_exit): ...this new function.
  (__main__): Add new command-line parsing logic, including support of
    new options (--username, --hostname, --password, --dbname) and the
    replacement of the --bonsai-compatible option with a more generic
    --version.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2079 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 18:18:46 +00:00
cmpilato
623ab85805 Merge to trunk all changes made on the issue-366-dev branch,
completing issue #366 (cvsdb purge operation is painfully slow).

* lib/cvsdb.py,
* bin/make-database


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2078 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 18:16:11 +00:00
cmpilato
ceb7057b9f Remove unnecessary empty svn:mergeinfo.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2077 8cb11bc2-c004-0410-86c3-e597b4017df7
2009-01-13 18:14:52 +00:00
cmpilato
b6486f5324 On the 'issue-366-dev' branch: add 'make-database --help' parameter.
* bin/make-database
  (BONSAI_COMPAT): Tweak a little bit.
  (__main__): Add support for a --help parameter that prints a usage message.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-366-dev@2076 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-22 21:22:46 +00:00
cmpilato
af83138e5c On the 'issue-366-dev' branch: Rename the 'checkins' table to
'commits' in schema version 1 (so that old version-ignorant code will
croak on a version-1 database).

* lib/cvsdb.py
  (CheckinDatabase.AddCommit, CheckinDatabase.CreateSQLQueryString,
   CheckinDatabase.CheckCommit, CheckinDatabase.sql_delete,
   CheckinDatabase.PurgeRepository): Account for the fact that in
    version 1 of the commits database schema, the 'checkins' table has
    been renamed to 'commits'.

* bin/make-database
  (DATABASE_SCRIPT_VERSION_1): Rename the 'checkins' table to 'commits'.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-366-dev@2075 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-22 21:04:12 +00:00
cmpilato
8f8dea6051 On the 'issue-366-dev' branch: Add a new index to version 1 of the
commits database schema, and use it (when present) in the 'purge'
operation.

* lib/cvsdb.py
  (CheckinDatabase.sql_purge): New function.
  (CheckinDatabase.PurgeRepository): If we have a schema version that
    supports it, use a much faster approach of purging.  Otherwise, we
    have to suffer through a slow purge process.

* bin/make-database
  (DATABASE_SCRIPT_VERSION_1): Add a new index of 'descid's to the
    'checkins' table.

Patch (mostly) by: Larry Shatzer, Jr. <larrys@gmail.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-366-dev@2074 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-22 15:38:09 +00:00
cmpilato
ba9cf67fae On the issue-366-dev branch: Add a new version of the commits database
schema that includes a `metadata' table (in which may be found the
schema version).

* lib/cvsdb.py
  (CVSDB_VERSION): New global variable.
  (CheckinDatabase.__init__): Init a new _version variable.
  (CheckinDatabase.Connect): Fetch a list of tables and see if the new
    `metadata' table is present.  If so, use it to query the database
    schema version.  (Else, assume version 0.)
  (CheckinDatabase.GetTableList, CheckinDatabase.GetMetadataValue,
   CheckinDatabase.SetMetadataValue): New functions.

* bin/make-database
  Rework this to be able to write two different schemas -- the
  original, Bonsai-compatible one, and a new one -- triggered by the
  presence/absence of a command-line --bonsai-compatible parameter.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-366-dev@2073 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-22 15:30:16 +00:00
cmpilato
09a5adb85d Create branch for issue #366 (cvsdb purge operation is painfully slow) work.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/issue-366-dev@2072 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-22 15:23:18 +00:00
cmpilato
0b1ac97f75 Try to deal with Subversion versions that lack the
SVN_ERR_CEASE_INVOCATION error code.

* lib/vclib/svn/svn_repos.py
  (_SVN_ERR_CEASE_INVOCATION): New compatibility variable.
  (NodeHistory.add_history, _get_history): Use compatibility variable
    instead of testing for SVN_ERR_CEASE_INVOCATION directly.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2070 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-19 19:48:43 +00:00
cmpilato
3e77755819 Document new num_changes item in upgrade notes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2068 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-19 19:45:44 +00:00
cmpilato
1433941124 Expose to revision.ezt a count of the total number of changed paths.
* lib/viewvc.py
  (view_revision): Add new 'num_changes' data dictionary item.

* templates/revision.ezt
  Expose new 'num_changes' item as a header on this page, and tweak
  the way the more/first changes links appear.

* docs/template-authoring-guide.html
  Document num_changes dictionary item.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2067 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-19 19:43:18 +00:00
cmpilato
c733fceb9f Rework the 'rlog-ended-early' FAQ bit with a more generic entry.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2066 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-10 18:12:29 +00:00
cmpilato
ca5a2bfbc9 Fix issue #385 (Hide/show annotations links mismanaged for binary
files).

* lib/viewvc.py
  (markup_or_annotate): Make the default value for the 'annotation'
    data dictionary member 'none' (instead of None) to simplify
    template logic.

* docs/template-authoring-guide.html
  Update docs for 'annotated'.

* templates/file.ezt
  Use simplified logic to avoid missing links.  (No, not *that* kind ...)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2064 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-10 17:26:37 +00:00
cmpilato
77fd2759c7 Remove unnecessary module imports.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2063 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-10 17:25:11 +00:00
cmpilato
08a8b110ac Allow admins to enable/disable diff/patch views via the allowed_views
configuration bit.

* lib/viewvc.py
  (view_diff, view_patch): Only show diffs if 'diff' is one of the
    allowed views.

* lib/config.py
  (Config.set_defaults): Add 'diff' to the set of allowed_views (and
    sort the values alphabetically where here).

* viewvc.conf.dist
  (allowed_views): Add 'diff' to the set of views you can specify, and
    include it in the default values.  While here, sort the values
    alphabetically.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2058 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-12-05 17:01:39 +00:00
cmpilato
38a7338c9d Add a configuration option for telling ViewVC to *not* honor the
svn:mime-type property.

* lib/config.py
  (Config.set_defaults): Set options.svn_ignore_mimetype default value.

* viewvc.conf.dist
  (svn_ignore_mimetype): New.

* lib/viewvc.py
  (calculate_mime_type): Consult cfg.options.svn_ignore_mimetype
    before looking up the 'svn:mime-type' property.

* docs/upgrading-howto.html
  Add a note about the new configuration option.

Patch (mostly) by: JJ <eggsgloriouseggs {_AT_} gmail.com>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2055 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 21:44:15 +00:00
cmpilato
bf40ef2683 It's very confusing to comment out the authorizer configuration line
and have that mean "use an authorizer".  So let's just prevent that
confusion right now, okay?

* lib/config.py
  (Config.set_defaults): Set 'authorizer' by default to None.

* viewvc.conf.dist
  (authorizer): Update comments for correctness, and don't imply that
    "forbidden" is the default authorizer.

* docs/upgrading-howto.html
  Stop saying that "forbidden" is the default authorizer.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2053 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 21:20:29 +00:00
cmpilato
11f5685955 Finish issue #384 - repos._revinfo_raw() is doing far too much work
when no authz is in use.  Vastly improves performance of
repos.itemlog() for local Subversion repositories *not* protected by
an authorizer.

* lib/vclib/svn/svn_repos.py
  (LocalSubversionRepository._revinfo): Was ._revinfo_raw().  Now
    encapsulates the caching logic that used to live in .revinfo(), plus
    the old logic of _revinfo_raw() modified to avoid changed-path
    processing when that processing isn't strictly required by callers.
  (LocalSubversionRepository.revinfo): Make this just a thin wrapper
    around ._revinfo() now.  Internal callers of this function updated
    to use ._revinfo() instead.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2051 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 21:14:14 +00:00
cmpilato
4904e03324 Minor improvements to the debug timer code.
* lib/debug.py
  (t_dump): Was dump().  Now accepts an output file handle, and sorts
    the output in a predictable fashion.

* lib/viewvc.py
  (main): Track changes to debug.dump().

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2048 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-13 19:45:13 +00:00
cmpilato
bd1254aa5f Avoid returning huge lists of variables from get_file_view_info(), and
use an ad-hoc class to hold all that stuff instead.

* lib/viewvc.py
  (get_file_view_info): Now return an object with interesting member variables
    instead of a nasty long list of values in an easy-to-goof-up order.
  (common_template_data, view_directory, view_log, view_diff): Update
    uses of get_file_view_info to track new calling semantics.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2047 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-11 16:21:58 +00:00
cmpilato
8b6e568ae1 * viewvc.org/faq.html: Minor wording tweak.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2046 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-10 22:05:27 +00:00
cmpilato
bc13ce9938 * viewvc.org/faq.html: Update the answer to the authz question.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2045 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-10 14:29:47 +00:00
cmpilato
0ddf057bbc Minor website tweakage.
* viewvc.org/index.html
  Lose 'Future Plans' section, and overhaul the feature and requirements lists.

* viewvc.org/contributing.html
  Fix the link to the Template Authoring Guide.

* viewvc.org/download.html
  Add a section for upgrading existing ViewVC installations.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2044 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-06 20:36:37 +00:00
cmpilato
5dbdea76ce docs/upgrading-howto.html: Don't claim as removed options not in 1.0.x.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2043 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-05 21:13:54 +00:00
cmpilato
8a9b9562f3 Merge r2040 from the 1.1.x branch:
* CHANGES: Combine some changes, and remove a now-bogus one.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2041 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-05 16:31:47 +00:00
cmpilato
51994aabc9 Doh! Remove empty <div> section.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2039 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-04 21:45:28 +00:00
cmpilato
f3edee505d Website and CHANGES file updates.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2038 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-11-04 21:44:44 +00:00
cmpilato
bcfb8f2c97 * lib/viewvc.py (__version__): Bump to '1.2-dev'.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@2034 8cb11bc2-c004-0410-86c3-e597b4017df7
2008-10-30 17:37:04 +00:00
200 changed files with 10982 additions and 5697 deletions

226
CHANGES
View File

@@ -1,4 +1,184 @@
Version 1.1.0 (released ??-???-????)
Version 1.2.0 (released ??-???-????)
* bumped minimum support Python version to 2.4
* implemented support for property diffs (issue #383)
* allow user-configurable cvsgraph display (issue #336)
* allow rNNNN syntax for Subversion revision numbers (issue #441)
Version 1.1.20 (released 24-Apr-2013)
* fix tab-to-space handling regression in markup view
* fix regression in root lookup handling (issue #526)
Version 1.1.19 (released 22-Apr-2013)
* improve root lookup performance (issue #523)
* new 'max_filesize_kbytes' config option and handling (issue #524)
* tarball generation improvements:
- preserve Subversion symlinks in generated tarballs (issue #487)
- reduce memory usage of tarball generation logic
- fix double compression of generated tarballs (issue #525)
* file content handling improvements:
- expanded support for encoding detection and transcoding (issue #11)
- fix tab-to-space conversion bugs in markup, annotate, and diff views
- fix handling of trailing whitespace in diff view
* add support for timestamp display in ISO8601 format (issue #46)
Version 1.1.18 (released 28-Feb-2013)
* fix exception raised by BDB-backed SVN repositories (issue #519)
* hide revision-less files when rcsparse is in use
* include branchpoints in branch views using rcsparse (issue #347)
* miscellaneous cvsdb improvements:
- add --port option to make-database (issue #521)
- explicitly name columns in queries (issue #522)
- update MySQL syntax to avoid discontinued "TYPE=" terms
Version 1.1.17 (released 25-Oct-2012)
* fix exception caused by uninitialized variable usage (issue #516)
Version 1.1.16 (released 24-Oct-2012)
* security fix: escape "extra" diff info to avoid XSS attack (issue #515)
* add 'binary_mime_types' configuration option and handling (issue #510)
* fix 'select for diffs' persistence across log pages (issue #512)
* remove lock status and filesize check on directories in remote SVN views
* fix bogus 'Annotation of' page title for non-annotated view (issue #514)
Version 1.1.15 (released 22-Jun-2012)
* security fix: complete authz support for remote SVN views (issue #353)
* security fix: log msg leak in SVN revision view with unreadable copy source
* fix several instances of incorrect information in remote SVN views
* increase performance of some revision metadata lookups in remote SVN views
* fix RSS feed regression introduced in 1.1.14
Version 1.1.14 (released 12-Jun-2012)
* fix annotation of svn files with non-URI-safe paths (issue #504)
* handle file:/// Subversion rootpaths as local roots (issue #446)
* fix bug caused by trying to case-normalize anon usernames (issue #505)
* speed up log handling by reusing tokenization results (issue #506)
* add support for custom revision log markup rules (issue #246)
Version 1.1.13 (released 23-Jan-2012)
* fix svndbadmin failure on deleted paths under Subversion 1.7 (issue #499)
* fix annotation of files in svn roots with non-URI-safe paths
* fix stray annotation warning in markup display of images
* more gracefully handle attempts to display binary content (issue #501)
Version 1.1.12 (released 03-Nov-2011)
* fix path display in patch and certain diff views (issue #485)
* fix broken cvsdb glob searching (issue 486)
* allow svn revision specifiers to have leading r's (issue #441, #448)
* allow environmental override of configuration location (issue #494)
* fix exception HTML-escaping non-string data under WSGI (issue #454)
* add links to root logs from roots view (issue #470)
* use Pygments lexer-guessing functionality (issue #495)
Version 1.1.11 (released 17-May-2011)
* security fix: remove user-reachable override of cvsdb row limit
* fix broken standalone.py -c and -d options handling
* add --help option to standalone.py
* fix stack trace when asked to checkout a directory (issue #478)
* improve memory usage and speed of revision log markup (issue #477)
* fix broken annotation view in CVS keyword-bearing files (issue #479)
* warn users when query results are incomplete (issue #433)
* avoid parsing errors on RCS newphrases in the admin section (issue #483)
* make rlog parsing code more robust in certain error cases (issue #444)
Version 1.1.10 (released 15-Mar-2011)
* fix stack trace in Subversion revision info logic (issue #475, issue #476)
Version 1.1.9 (released 18-Feb-2011)
* vcauth universal access determinations (issue #425)
* rework svn revision info cache for performance
* make revision log "extra pages" count configurable
* fix Subversion 1.4.x revision log compatibility code regression
* display sanitized error when authzfile is malformed
* restore markup of URLs in file contents (issue #455)
* optionally display last-committed metadata in roots view (issue #457)
Version 1.1.8 (released 02-Dec-2010)
* fix slowness triggered by allow_compress=1 configuration (issue #467)
* allow use of 'fcrypt' for Windows standalone.py authn support (issue #471)
* yield more useful error on directory markup/annotate request (issue #472)
Version 1.1.7 (released 09-Sep-2010)
* display Subversion revision properties in the revision view (issue #453)
* fix exception in 'standalone.py -r REPOS' when run without a config file
* fix standalone.py server root deployments (--script-alias='')
* add rudimentary Basic authentication support to standalone.py (issue #49)
* fix obscure "unexpected NULL parent pool" Subversion bindings error
* enable path info / link display in remote Subversion root revision view
* fix vhost name case handling inconsistency (issue #466)
* use svn:mime-type property charset param as encoding hint
* markup Subversion revision references in log messages (issue #313)
* add rudimentary support for FastCGI-based deployments (issue #464)
* fix query script WSGI deployment
* add configuration to fix query script cross-linking to ViewVC
Version 1.1.6 (released 02-Jun-2010)
* add rudimentary support for WSGI-based deployments (issue #397)
* fix exception caused by trying to HTML-escape non-string data (issue #454)
* fix incorrect RSS feed Content-Type header (issue #449)
* fix RSS <title> encoding problem (issue #451)
* allow 'svndbadmin purge' to work on missing repositories (issue #452)
Version 1.1.5 (released 29-Mar-2010)
* security fix: escape user-provided search_re input to avoid XSS attack
Version 1.1.4 (released 10-Mar-2010)
* security fix: escape user-provided query form input to avoid XSS attack
* fix standalone.py failure (when per-root options aren't used) (issue #445)
* fix annotate failure caused by ignored svn_config_dir (issue #447)
Version 1.1.3 (released 22-Dec-2009)
* security fix: add root listing support of per-root authz config
* 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
* security fix: avoid printing illegal parameter names and values
* add optional support for character encoding detection (issue #400)
* fix username case handling in svnauthz module (issue #419)
* fix cvsdbadmin/svnadmin rebuild error on missing repos (issue #420)
* don't drop leading blank lines from colorized file contents (issue #422)
* add file.ezt template logic for optionally hiding binary file contents
Version 1.1.1 (released 03-Jun-2009)
* fix broken query form (missing required template variables) (issue #416)
* fix bug in cvsdb which caused rebuild operations to lose data (issue #417)
* fix cvsdb purge/rebuild repos lookup to error on missing repos
* fix misleading file contents view page title
Version 1.1.0 (released 13-May-2009)
* add support for full content diffs (issue #153)
* make many more data dictionary items available to all views
@@ -12,9 +192,7 @@ Version 1.1.0 (released ??-???-????)
* add support for query by log message (issues #22, #121)
* fix bug parsing 'svn blame' output with too-long author names (issue #221)
* fix default standalone.py port to be within private IANA range (issue #234)
* add support for integration with GNU source-highlight (issue #285)
* add unified configury of allowed views
* add support for disabling the checkout view (now the default state)
* add unified configury of allowed views; checkout view disabled by default
* add support for ranges of revisions to svndbadmin (issue #224)
* make the query handling more forgiving of malformatted subdirs (issue #244)
* add support for per-root configuration overrides (issue #371)
@@ -36,7 +214,45 @@ Version 1.1.0 (released ??-???-????)
* show RSS/query links only for roots found in commits database (issue #357)
* recognize Subversion svn:mime-type property values (issue #364)
* hide CVS files when viewing tags/branches on which they don't exist
* add support for hiding errorful entries from the directory view (issue #105)
* allow hiding of errorful entries from the directory view (issue #105)
* fix directory view sorting UI
* tolerate malformed Accept-Language headers (issue #396)
* allow MIME type mapping overrides in ViewVC configuration (issue #401)
* fix exception in rev-sorted remote Subversion directory views (issue #409)
* allow setting of page sizes for log and dir views individually (issue #402)
Version 1.0.13 (released 24-Oct-2012)
* security fix: escape "extra" diff info to avoid XSS attack (issue #515)
* security fix: remove user-reachable override of cvsdb row limit
* fix obscure "unexpected NULL parent pool" Subversion bindings error
* fix svndbadmin failure on deleted paths under Subversion 1.7 (issue #499)
Version 1.0.12 (released 02-Jun-2010)
* fix exception caused by trying to HTML-escape non-string data (issue #454)
Version 1.0.11 (released 29-Mar-2010)
* security fix: escape user-provided search_re input to avoid XSS attack
Version 1.0.10 (released 10-Mar-2010)
* security fix: escape user-provided query form input to avoid XSS attack
* fix errors viewing remote Subversion paths with URI-unsafe characters
* fix regexp input validation (issue #426, #427, #440)
Version 1.0.9 (released 11-Aug-2009)
* security fix: validate the 'view' parameter to avoid XSS attack
* security fix: avoid printing illegal parameter names and values
Version 1.0.8 (released 05-May-2009)
* fix directory view sorting UI
* tolerate malformed Accept-Language headers (issue #396)
* fix directory log views in revision-less Subversion repositories
* fix exception in rev-sorted remote Subversion directory views (issue #409)
Version 1.0.7 (released 14-Oct-2008)

205
INSTALL
View File

@@ -17,10 +17,13 @@ Congratulations on getting this far. :-)
Required Software And Configuration Needed To Run ViewVC:
In General:
* Python 2, version 2.4 or later (sorry, no 3.x support yet)
(http://www.python.org/)
For CVS Support:
* Python 1.5.2 or later
(http://www.python.org/)
* RCS, Revision Control System
(http://www.cs.purdue.edu/homes/trinkle/RCS/)
* GNU-diff to replace diff implementations without the -u option
@@ -30,11 +33,9 @@ Congratulations on getting this far. :-)
For Subversion Support:
* Python 2.0 or later
(http://www.python.org/)
* Subversion, Version Control System, 1.3.1 or later
(binary installation and Python bindings)
(http://subversion.tigris.org/)
(http://subversion.apache.org/)
Optional:
@@ -139,7 +140,7 @@ installation instructions.
default_root
root_as_url_component
rcs_dir
mime_types_file
mime_types_files
There are some other options that are usually nice to change. See
viewvc.conf for more information. ViewVC provides a working,
@@ -168,72 +169,185 @@ 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) Depending on how your Apache configuration is setup by default, you
might need to explicitly allow high-level access to the ViewVC
install location.
<Directory <VIEWVC_INSTALLATION_DIRECTORY>>
Order allow,deny
Allow from all
</Directory>
For example, if ViewVC is installed in /usr/local/viewvc-1.0 on
your system:
<Directory /usr/local/viewvc-1.0>
Order allow,deny
Allow from all
</Directory>
3) 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
ScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/viewvc.cgi
into your httpd.conf file. Choose the location in httpd.conf where
also the other ScriptAlias lines reside. Some examples:
ScriptAlias /viewvc /usr/local/viewvc-1.0/bin/cgi/viewvc.cgi
ScriptAlias /query /usr/local/viewvc-1.0/bin/cgi/query.cgi
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).
You can override configuration file location using:
SetEnv VIEWVC_CONF_PATHNAME /etc/viewvc.conf
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
Options +ExecCGI
AddHandler cgi-script .cgi
(Note: For this to work mod_cgi has to be loaded. And for the .htaccess file
Note: For this to work mod_cgi has to be loaded. And for the .htaccess file
to be effective, "AllowOverride All" or "AllowOverride Options FileInfo"
need to have been specified for the directory.)
needs 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
you copied the files to.
You can override configuration file location using:
SetEnv VIEWVC_CONF_PATHNAME /etc/viewvc.conf
Note: If you are using Mod_Python under Apache 1.3 the tarball generation
feature may not work because it uses multithreading. This works fine
under Apache 2.
continue with step 3).
----------------------------------------
METHOD E: Using mod_wsgi (if installed)
----------------------------------------
Copy the Python scripts file from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/
to the directory of your choosing. Modify httpd.conf with the
following directives:
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"
WSGIScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/viewvc.wsgi
WSGIScriptAlias /query <VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/query.wsgi
4) Optional: Add access control.
You'll probably also need the following directive because of the
not-quite-sanctioned way that ViewVC manipulates Python objects.
In your httpd.conf you can control access to certain modules by adding
directives like this:
WSGIApplicationGroup %{GLOBAL}
Note: WSGI support in ViewVC is at this time quite rudimentary,
bordering on downright experimental. Your mileage may vary.
-----------------------------------------
METHOD F: Using mod_fcgid (if installed)
-----------------------------------------
This uses ViewVC's WSGI support (from above), but supports using FastCGI,
and is a somewhat hybrid approach of several of the above methods.
Especially if fcgi is already being used for other purposes, e.g. PHP,
also using fcgi can prevent the need for including additional modules
(e.g. mod_python or mod_wsgi) within Apache, which may help lessen Apache's
memory usage and/or help improve performance.
This depends on mod_fcgid:
http://httpd.apache.org/mod_fcgid/
as well as the fcgi server from Python's flup package:
http://pypi.python.org/pypi/flup
http://trac.saddi.com/flup
The following are some example httpd.conf fragments you can use to
support this configuration:
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
ScriptAlias /query /usr/local/viewvc/bin/wsgi/query.fcgi
4) [Optional] Provide direct access to icons, stylesheets, etc.
ViewVC's HTML templates reference various stylesheets and icons
provided by ViewVC itself. By default, ViewVC generates URLs to
those artifacts which point back into ViewVC (using a magic
syntax); ViewVC in turn handles such magic URL requests by
streaming back the contents of the requested icon or stylesheet
file. While this simplifies the configuration and initial
deployment of ViewVC, it's not the most efficient approach to
deliver what is essentially static content.
To improve performance, consider carving out a URL space in your
webserver's configuration solely for this static content and
instruct ViewVC to use that space when generating URLs for that
content. For example, you might add an Alias such as the following
to your httpd.conf:
Alias /viewvc-docroot /usr/local/viewvc/templates/default/docroot
And then, in viewvc.conf, set the 'docroot' option to the same
location:
docroot = /viewvc-docroot
WARNING: As always when using Alias directives, be careful that you
have them in the correct order. For example, if you use an
ordering such as the following, Apache will hand requests for your
static documents off to ViewVC as if they were versioned resources:
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
Alias /viewvc/static /usr/local/viewvc/templates/default/docroot
The correct order would be:
Alias /viewvc/static /usr/local/viewvc/templates/default/docroot
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
(That said, it's best to avoid such namespace nesting altogether if
you can.)
5) [Optional] Add access control.
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
@@ -251,7 +365,14 @@ or if you've got Mod_Python installed you can use METHOD D:
http://<server_name>/viewvc/~checkout~/<module_name>
http://<server_name>/viewvc/<module_name>.tar.gz?view=tar
5) Optional: Protect your ViewVC instance from server-whacking webcrawlers.
6) Restart Apache.
The commands to do this vary. "httpd -k restart" and "apache -k
restart" are two common variants. On RedHat Linux it is done using
the command "/sbin/service httpd restart" and on SuSE Linux it is
done with "rcapache restart". Other systems use "apachectl restart".
7) [Optional] Protect your ViewVC instance from server-whacking webcrawlers.
As ViewVC is a web-based application which each page containing various
links to other pages and views, you can expect your server's performance

View File

@@ -15,7 +15,7 @@
<blockquote>
<p><strong>Copyright &copy; 1999-2008 The ViewCVS Group. All rights
<p><strong>Copyright &copy; 1999-2013 The ViewCVS Group. All rights
reserved.</strong></p>
<p>By using ViewVC, you agree to the terms and conditions set forth
@@ -59,6 +59,11 @@
<li>March 17, 2006 &mdash; software renamed from "ViewCVS"</li>
<li>April 10, 2007 &mdash; copyright years updated</li>
<li>February 22, 2008 &mdash; copyright years updated</li>
<li>March 18, 2009 &mdash; copyright years updated</li>
<li>March 29, 2010 &mdash; copyright years updated</li>
<li>February 18, 2011 &mdash; copyright years updated</li>
<li>January 23, 2012 &mdash; copyright years updated</li>
<li>January 04, 2013 &mdash; copyright years updated</li>
</ul>
</body>

View File

@@ -3,7 +3,7 @@
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -54,7 +54,10 @@ import query
server = sapi.AspServer(Server, Request, Response, Application)
try:
cfg = viewvc.load_config(CONF_PATHNAME, server)
query.main(server, cfg, "viewvc.asp")
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.asp"
query.main(server, cfg, viewvc_base_url)
finally:
s.close()

View File

@@ -3,7 +3,7 @@
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -34,7 +34,7 @@ CONF_PATHNAME = None
#########################################################################
#
# Adjust sys.path to include our library directory
# Adjust sys.path to include our library directory.
#
import sys
@@ -47,6 +47,20 @@ else:
"../../../lib")))
#########################################################################
#
# If admins want nicer processes, here's the place to get them.
#
#try:
# os.nice(20) # bump the nice level of this process
#except:
# pass
#########################################################################
#
# Go do the work.
#
import sapi
import viewvc
@@ -54,4 +68,7 @@ import query
server = sapi.CgiServer()
cfg = viewvc.load_config(CONF_PATHNAME, server)
query.main(server, cfg, "viewvc.cgi")
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.cgi"
query.main(server, cfg, viewvc_base_url)

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -34,7 +34,7 @@ CONF_PATHNAME = None
#########################################################################
#
# Adjust sys.path to include our library directory
# Adjust sys.path to include our library directory.
#
import sys
@@ -47,12 +47,21 @@ else:
"../../../lib")))
#########################################################################
#
# If admins want nicer processes, here's the place to get them.
#
#try:
# os.nice(20) # bump the nice level of this process
#except:
# pass
### add code for checking the load average
#########################################################################
#
# Go do the work.
#
# go do the work
import sapi
import viewvc

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -40,7 +40,6 @@ else:
#########################################################################
import os
import string
import cvsdb
import viewvc
import vclib.ccvs
@@ -56,7 +55,7 @@ def UpdateFile(db, repository, path, update, quiet_level):
print '[ERROR] %s' % (e)
return
file = string.join(path, "/")
file = '/'.join(path)
printing = 0
if update:
if quiet_level < 1 or (quiet_level < 2 and len(commit_list)):
@@ -177,7 +176,12 @@ if __name__ == '__main__':
if command in ('rebuild', 'purge'):
if quiet_level < 2:
print "Purging existing data for repository root `%s'" % root
db.PurgeRepository(root)
try:
db.PurgeRepository(root)
except cvsdb.UnknownRepositoryError, e:
if command == 'purge':
sys.stderr.write("ERROR: " + str(e) + "\n")
sys.exit(1)
if command in ('rebuild', 'update'):
repository = vclib.ccvs.CVSRepository(None, rootpath, None,

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -39,7 +39,6 @@ else:
#########################################################################
import os
import string
import getopt
import re
import cvsdb
@@ -152,11 +151,11 @@ def FindLongestDirectory(s, repository):
and a file name, either of which may contain spaces. Returns the longest
possible directory name that actually exists"""
parts = string.split(s, " ")
parts = s.split()
for i in range(len(parts)-1, 0, -1):
directory = string.join(parts[:i])
filename = string.join(parts[i:])
directory = ' '.join(parts[:i])
filename = ' '.join(parts[i:])
if os.path.isdir(os.path.join(repository, directory)):
return directory, filename
@@ -227,7 +226,7 @@ def ProcessLoginfo(rootpath, directory, files):
cfg.utilities, 0)
# split up the directory components
dirpath = filter(None, string.split(os.path.normpath(directory), os.sep))
dirpath = filter(None, os.path.normpath(directory).split(os.sep))
## build a list of Commit objects
commit_list = []
@@ -279,7 +278,7 @@ if __name__ == '__main__':
else:
# if there are no arguments, read version information from
# first line of input like old versions of ViewCVS did
arg = string.rstrip(sys.stdin.readline())
arg = sys.stdin.readline().rstrip()
if len(sys.argv) > 2:
# if there is a second argument it indicates which parser

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -16,31 +16,32 @@
#
# -----------------------------------------------------------------------
import os, sys, string
import os
import sys
import popen2
import getopt
INTRO_TEXT = """\
This script creates the database and tables in MySQL used by the
ViewVC checkin database. You will be prompted for: database server
hostname, database user, database user password, and database name.
This script will use the 'mysql' program to create the database for
you. You will then need to set the appropriate parameters in the
[cvsdb] section of your viewvc.conf file.
"""
DATABASE_SCRIPT="""\
## ------------------------------------------------------------------------
## Stuff common to all schemas
##
DATABASE_SCRIPT_COMMON="""\
DROP DATABASE IF EXISTS <dbname>;
CREATE DATABASE <dbname>;
USE <dbname>;
"""
## ------------------------------------------------------------------------
## Version 0: The original, Bonsai-compatible schema.
##
DATABASE_SCRIPT_VERSION_0="""\
DROP TABLE IF EXISTS branches;
CREATE TABLE branches (
id mediumint(9) NOT NULL auto_increment,
branch varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE branch (branch)
) TYPE=MyISAM;
) ENGINE=MyISAM;
DROP TABLE IF EXISTS checkins;
CREATE TABLE checkins (
@@ -63,7 +64,7 @@ CREATE TABLE checkins (
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
) TYPE=MyISAM;
) ENGINE=MyISAM;
DROP TABLE IF EXISTS descs;
CREATE TABLE descs (
@@ -72,7 +73,7 @@ CREATE TABLE descs (
hash bigint(20) DEFAULT '0' NOT NULL,
PRIMARY KEY (id),
KEY hash (hash)
) TYPE=MyISAM;
) ENGINE=MyISAM;
DROP TABLE IF EXISTS dirs;
CREATE TABLE dirs (
@@ -80,7 +81,7 @@ CREATE TABLE dirs (
dir varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE dir (dir)
) TYPE=MyISAM;
) ENGINE=MyISAM;
DROP TABLE IF EXISTS files;
CREATE TABLE files (
@@ -88,7 +89,7 @@ CREATE TABLE files (
file varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE file (file)
) TYPE=MyISAM;
) ENGINE=MyISAM;
DROP TABLE IF EXISTS people;
CREATE TABLE people (
@@ -96,7 +97,7 @@ CREATE TABLE people (
who varchar(128) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE who (who)
) TYPE=MyISAM;
) ENGINE=MyISAM;
DROP TABLE IF EXISTS repositories;
CREATE TABLE repositories (
@@ -104,7 +105,7 @@ CREATE TABLE repositories (
repository varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE repository (repository)
) TYPE=MyISAM;
) ENGINE=MyISAM;
DROP TABLE IF EXISTS tags;
CREATE TABLE tags (
@@ -118,31 +119,232 @@ CREATE TABLE tags (
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
) TYPE=MyISAM;
) ENGINE=MyISAM;
"""
## ------------------------------------------------------------------------
## Version 1: Adds the 'metadata' table. Adds 'descid' index to
## 'checkins' table, and renames that table to 'commits'.
##
DATABASE_SCRIPT_VERSION_1="""\
DROP TABLE IF EXISTS branches;
CREATE TABLE branches (
id mediumint(9) NOT NULL auto_increment,
branch varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE branch (branch)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS commits;
CREATE TABLE commits (
type enum('Change','Add','Remove'),
ci_when datetime DEFAULT '0000-00-00 00:00:00' NOT NULL,
whoid mediumint(9) DEFAULT '0' NOT NULL,
repositoryid mediumint(9) DEFAULT '0' NOT NULL,
dirid mediumint(9) DEFAULT '0' NOT NULL,
fileid mediumint(9) DEFAULT '0' NOT NULL,
revision varchar(32) binary DEFAULT '' NOT NULL,
stickytag varchar(255) binary DEFAULT '' NOT NULL,
branchid mediumint(9) DEFAULT '0' NOT NULL,
addedlines int(11) DEFAULT '0' NOT NULL,
removedlines int(11) DEFAULT '0' NOT NULL,
descid mediumint(9),
UNIQUE repositoryid (repositoryid,dirid,fileid,revision),
KEY ci_when (ci_when),
KEY whoid (whoid),
KEY repositoryid_2 (repositoryid),
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid),
KEY descid (descid)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS descs;
CREATE TABLE descs (
id mediumint(9) NOT NULL auto_increment,
description text,
hash bigint(20) DEFAULT '0' NOT NULL,
PRIMARY KEY (id),
KEY hash (hash)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS dirs;
CREATE TABLE dirs (
id mediumint(9) NOT NULL auto_increment,
dir varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE dir (dir)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS files;
CREATE TABLE files (
id mediumint(9) NOT NULL auto_increment,
file varchar(255) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE file (file)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS people;
CREATE TABLE people (
id mediumint(9) NOT NULL auto_increment,
who varchar(128) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE who (who)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS repositories;
CREATE TABLE repositories (
id mediumint(9) NOT NULL auto_increment,
repository varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE repository (repository)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS tags;
CREATE TABLE tags (
repositoryid mediumint(9) DEFAULT '0' NOT NULL,
branchid mediumint(9) DEFAULT '0' NOT NULL,
dirid mediumint(9) DEFAULT '0' NOT NULL,
fileid mediumint(9) DEFAULT '0' NOT NULL,
revision varchar(32) binary DEFAULT '' NOT NULL,
UNIQUE repositoryid (repositoryid,dirid,fileid,branchid,revision),
KEY repositoryid_2 (repositoryid),
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
) ENGINE=MyISAM;
DROP TABLE IF EXISTS metadata;
CREATE TABLE metadata (
name varchar(255) binary DEFAULT '' NOT NULL,
value text,
PRIMARY KEY (name),
UNIQUE name (name)
) ENGINE=MyISAM;
INSERT INTO metadata (name, value) VALUES ('version', '1');
"""
BONSAI_COMPAT="""
WARNING: Creating Bonsai-compatible legacy database version. Some ViewVC
features may not be available, or may not perform especially well.
"""
## ------------------------------------------------------------------------
def usage_and_exit(errmsg=None):
stream = errmsg is None and sys.stdout or sys.stderr
stream.write("""\
Usage: %s [OPTIONS]
This script creates the database and tables in MySQL used by the
ViewVC checkin database. In order to operate correctly, it needs to
know the following: your database server hostname, database user,
database user password, and database name. (You will be prompted for
any of this information that you do not provide via command-line
options.) This script will use the 'mysql' program to create the
database for you. You will then need to set the appropriate
parameters in the [cvsdb] section of your viewvc.conf file.
NOTE: If a hostname or port is supplied at the command line or during
interactive prompting, this script will pass '--protocol=TCP' to
'mysql'.
Options:
--dbname=ARG Use ARG as the ViewVC database name to create.
[Default: ViewVC]
--help Show this usage message.
--hostname=ARG Use ARG as the hostname for the MySQL connection.
--port=ARG Use ARG as the port for the MySQL connection.
--password=ARG Use ARG as the password for the MySQL connection.
--username=ARG Use ARG as the username for the MySQL connection.
--version=ARG Create the database using the schema employed by
version ARG of ViewVC. Valid values are:
[ "1.0" ]
""" % (os.path.basename(sys.argv[0])))
if errmsg is not None:
stream.write("[ERROR] %s.\n" % (errmsg))
sys.exit(1)
sys.exit(0)
if __name__ == "__main__":
try:
print INTRO_TEXT
# Parse the command-line options, if any.
dbname = version = hostname = port = username = password = None
opts, args = getopt.getopt(sys.argv[1:], '', [ 'dbname=',
'help',
'hostname=',
'port=',
'password=',
'username=',
'version=',
])
if len(args) > 0:
usage_and_exit("Unexpected command-line parameters")
for name, value in opts:
if name == '--help':
usage_and_exit(0)
elif name == '--dbname':
dbname = value
elif name == '--hostname':
hostname = value
elif name == '--port':
port = value
elif name == '--username':
username = value
elif name == '--password':
password = value
elif name == '--version':
if value in ["1.0"]:
version = value
else:
usage_and_exit("Invalid version specified")
# Prompt for necessary information
host = raw_input("MySQL Hostname [default: localhost]: ") or ""
user = raw_input("MySQL User: ")
passwd = raw_input("MySQL Password: ")
dbase = raw_input("ViewVC Database Name [default: ViewVC]: ") or "ViewVC"
# Prompt for information not provided via command-line options.
if hostname is None:
hostname = raw_input("MySQL Hostname (leave blank for default): ")
if port is None:
port = raw_input("MySQL Port (leave blank for default): ")
if username is None:
username = raw_input("MySQL User: ")
if password is None:
password = raw_input("MySQL Password: ")
if dbname is None:
dbname = raw_input("ViewVC Database Name [default: ViewVC]: ") or "ViewVC"
# Create the database
dscript = string.replace(DATABASE_SCRIPT, "<dbname>", dbase)
host_option = host and "--host=%s" % (host) or ""
# Create the database.
dscript = DATABASE_SCRIPT_COMMON.replace("<dbname>", dbname)
if version == "1.0":
print BONSAI_COMPAT
dscript = dscript + DATABASE_SCRIPT_VERSION_0
else:
dscript = dscript + DATABASE_SCRIPT_VERSION_1
# Calculate command arguments.
cmd_args = "--user=%s --password=%s" % (username, password)
if hostname or port:
cmd_args = cmd_args + " --protocol=TCP"
if hostname:
cmd_args = cmd_args + " --host=%s" % (hostname)
if port:
cmd_args = cmd_args + " --port=%s" % (port)
if sys.platform == "win32":
cmd = "mysql --user=%s --password=%s %s "\
% (user, passwd, host_option)
cmd = "mysql %s" % (cmd_args)
mysql = os.popen(cmd, "w") # popen2.Popen3 is not provided on windows
mysql.write(dscript)
status = mysql.close()
else:
cmd = "{ mysql --user=%s --password=%s %s ; } 2>&1" \
% (user, passwd, host_option)
cmd = "{ mysql %s ; } 2>&1" % (cmd_args)
pipes = popen2.Popen3(cmd)
pipes.tochild.write(dscript)
pipes.tochild.close()
@@ -150,10 +352,11 @@ if __name__ == "__main__":
status = pipes.wait()
if status:
print "[ERROR] the database did not create sucessfully."
print "[ERROR] The database did not create sucessfully."
sys.exit(1)
print "Database created successfully."
print "Database created successfully. Don't forget to configure the "
print "[cvsdb] section of your viewvc.conf file."
except KeyboardInterrupt:
pass
sys.exit(0)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -65,7 +65,10 @@ cfg = viewvc.load_config(CONF_PATHNAME)
def index(req):
server = sapi.ModPythonServer(req)
try:
query.main(server, cfg, "viewvc.py")
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.py"
query.main(server, cfg, viewvc_base_url)
finally:
server.close()

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 2004-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2004-2007 James Henstridge
#
# By using this file, you agree to the terms and conditions set forth in
@@ -56,7 +57,6 @@ else:
#########################################################################
import os
import string
import re
import svn.core
@@ -152,7 +152,7 @@ class SvnRev:
fsroot = self._get_root_for_rev(rev)
# find changes in the revision
editor = svn.repos.RevisionChangeCollector(repo.fs, rev)
editor = svn.repos.ChangeCollector(repo.fs, fsroot)
e_ptr, e_baton = svn.delta.make_editor(editor)
svn.repos.svn_repos_replay(fsroot, e_ptr, e_baton)
@@ -163,21 +163,33 @@ class SvnRev:
continue
# deal with the change types we handle
action = None
base_root = None
base_path = change.base_path
if change.base_path:
base_root = self._get_root_for_rev(change.base_rev)
if not change.path:
# figure out what kind of change this is, and get a diff
# object for it. note that prior to 1.4 Subversion's
# bindings didn't give us change.action, but that's okay
# because back then deleted paths always had a change.path
# of None.
if hasattr(change, 'action') \
and change.action == svn.repos.CHANGE_ACTION_DELETE:
action = 'remove'
elif not change.path:
action = 'remove'
elif change.added:
action = 'add'
else:
action = 'change'
diffobj = svn.fs.FileDiff(base_root and base_root or None,
base_root and change.base_path or None,
change.path and fsroot or None,
change.path and change.path or None)
if action == 'remove':
diffobj = svn.fs.FileDiff(base_root, base_path, None, None)
else:
diffobj = svn.fs.FileDiff(base_root, base_path,
fsroot, change.path)
diff_fp = diffobj.get_pipe()
plus, minus = _get_diff_counts(diff_fp)
self.changes.append((path, action, plus, minus))
@@ -241,23 +253,35 @@ def main(command, repository, revs=[], verbose=0, force=0):
cfg = viewvc.load_config(CONF_PATHNAME)
db = cvsdb.ConnectDatabase(cfg)
# Purge what must be purged.
if command in ('rebuild', 'purge'):
if verbose:
print "Purging commit info for repository root `%s'" % repository
db.PurgeRepository(repository)
try:
db.PurgeRepository(repository)
except cvsdb.UnknownRepositoryError, e:
if command == 'purge':
sys.stderr.write("ERROR: " + str(e) + "\n")
sys.exit(1)
repo = SvnRepo(repository)
if command == 'rebuild' or (command == 'update' and not revs):
for rev in range(repo.rev_max+1):
handle_revision(db, command, repo, rev, verbose)
elif command == 'update':
if revs[0] is None:
revs[0] = repo.rev_max
if revs[1] is None:
revs[1] = repo.rev_max
revs.sort()
for rev in range(revs[0], revs[1]+1):
handle_revision(db, command, repo, rev, verbose, force)
# Record what must be recorded.
if command in ('rebuild', 'update'):
if not os.path.exists(repository):
sys.stderr.write('ERROR: could not find repository %s\n'
% (repository))
sys.exit(1)
repo = SvnRepo(repository)
if command == 'rebuild' or (command == 'update' and not revs):
for rev in range(repo.rev_max+1):
handle_revision(db, command, repo, rev, verbose)
elif command == 'update':
if revs[0] is None:
revs[0] = repo.rev_max
if revs[1] is None:
revs[1] = repo.rev_max
revs.sort()
for rev in range(revs[0], revs[1]+1):
handle_revision(db, command, repo, rev, verbose, force)
def _rev2int(r):
if r == 'HEAD':
@@ -275,7 +299,7 @@ def usage():
located at REPOS-PATH.
Usage: 1. %s [-v] rebuild REPOS-PATH
2. %s [-v] update REPOS-PATH [REV:[REV2]] [--force]
2. %s [-v] update REPOS-PATH [REV[:REV2]] [--force]
3. %s [-v] purge REPOS-PATH
1. Rebuild the commit database information for the repository located
@@ -319,17 +343,11 @@ if __name__ == '__main__':
if len(args) < 3:
usage()
command = string.lower(args[1])
command = args[1].lower()
if command not in ('rebuild', 'update', 'purge'):
sys.stderr.write('ERROR: unknown command %s\n' % command)
usage()
repository = args[2]
if not os.path.exists(repository):
sys.stderr.write('ERROR: could not find repository %s\n' % args[2])
usage()
repository = vclib.svn.canonicalize_rootpath(repository)
revs = []
if len(sys.argv) > 3:
if command == 'rebuild':
@@ -352,6 +370,7 @@ if __name__ == '__main__':
rev = None
try:
repository = vclib.svn.canonicalize_rootpath(args[2])
repository = cvsdb.CleanRepository(os.path.abspath(repository))
main(command, repository, revs, verbose, force)
except KeyboardInterrupt:

54
bin/wsgi/query.fcgi Normal file
View File

@@ -0,0 +1,54 @@
#!/usr/bin/python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a fcgi entry point for the query ViewVC app. It's appropriate
# for use with mod_fcgid and flup. It defines a single application function
# that is a valid fcgi entry point.
#
# mod_fcgid: http://httpd.apache.org/mod_fcgid/
# flup:
# http://pypi.python.org/pypi/flup
# http://trac.saddi.com/flup
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
import query
from flup.server import fcgi
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.fcgi"
query.main(server, cfg, viewvc_base_url)
return []
fcgi.WSGIServer(application).run()

45
bin/wsgi/query.wsgi Normal file
View File

@@ -0,0 +1,45 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a wsgi entry point for the query ViewVC app. It's appropriate
# for use with mod_wsgi. It defines a single application function that
# is a valid wsgi entry point.
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
import query
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc_base_url = cfg.query.viewvc_base_url
if viewvc_base_url is None:
viewvc_base_url = "viewvc.wsgi"
query.main(server, cfg, viewvc_base_url)
return []

50
bin/wsgi/viewvc.fcgi Normal file
View File

@@ -0,0 +1,50 @@
#!/usr/bin/python
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a fcgi entry point for the main ViewVC app. It's appropriate
# for use with mod_fcgid and flup. It defines a single application function
# that is a valid fcgi entry point.
#
# mod_fcgid: http://httpd.apache.org/mod_fcgid/
# flup:
# http://pypi.python.org/pypi/flup
# http://trac.saddi.com/flup
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
from flup.server import fcgi
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc.main(server, cfg)
return []
fcgi.WSGIServer(application).run()

41
bin/wsgi/viewvc.wsgi Normal file
View File

@@ -0,0 +1,41 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# viewvc: View CVS/SVN repositories via a web browser
#
# -----------------------------------------------------------------------
#
# This is a wsgi entry point for the main ViewVC app. It's appropriate
# for use with mod_wsgi. It defines a single application function that
# is a valid wsgi entry point.
#
# -----------------------------------------------------------------------
import sys, os
LIBRARY_DIR = None
CONF_PATHNAME = None
if LIBRARY_DIR:
sys.path.insert(0, LIBRARY_DIR)
else:
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
"../../../lib")))
import sapi
import viewvc
def application(environ, start_response):
server = sapi.WsgiServer(environ, start_response)
cfg = viewvc.load_config(CONF_PATHNAME, server)
viewvc.main(server, cfg)
return []

32
conf/mimetypes.conf.dist Normal file
View File

@@ -0,0 +1,32 @@
#---------------------------------------------------------------------------
#
# MIME type mapping file for ViewVC
#
# Information on ViewVC is located at the following web site:
# http://viewvc.org/
#
#---------------------------------------------------------------------------
# THE FORMAT OF THIS FILE
#
# This file contains records -- one per line -- of the following format:
#
# MIME_TYPE [EXTENSION [EXTENSION ...]]
#
# where whitespace separates the MIME_TYPE from the EXTENSION(s),
# and the EXTENSIONs from each other.
#
# For example:
#
# text/x-csh csh
# text/x-csrc c
# text/x-diff diff patch
# image/png png
# image/jpeg jpeg jpg jpe
#
# By default, this file is left empty, allowing ViewVC to continue
# consulting it first without overriding the MIME type mappings
# found in more standard mapping files (such as those provided as
# part of the operating system or web server software).
#
#

1203
conf/viewvc.conf.dist Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,213 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewVC: 1.1.0 Release Notes</title>
<style>
.h2, .h3 {
padding: 0.25em 0em;
background: white;
}
.warning {
font-style: italic;
}
</style>
</head>
<body>
<h1>ViewVC 1.1.0 Release Notes</h1>
<div class="h2">
<h2 id="introduction">Introduction</h2>
<p>ViewVC 1.1.0 is the superset of all previous ViewVC releases.</p>
</div> <!-- h2 -->
<div class="h2">
<h2 id="compatibility">Compatibility</h2>
<p>Each ViewVC release strives to maintain URL stability with previous
releases, and 1.1.0 is no exception. All URLs considered valid for
previous ViewVC releases should continue to work correctly in this
release, though possibly only via the use of HTTP redirects
(generated by ViewVC itself).</p>
<p>The commits database functionality has changed in ViewVC 1.1.0 in
way that breaks compatibility with prior ViewVC releases, but only
for new database instantiations. ViewVC 1.1.0 will continue to
understand (for both read and write operations) the previous
schema, so you are not required to rebuild your commits database
for ViewVC 1.1.0 compatibility. By default, new commits databases
created using the 1.1.0 version of the <code>make-database</code>
script will use a new database schema that is unreadable by
previous ViewVC versions. However, if you need a database which
can co-exist with a previous ViewVC version, you can use
the <code>--version=1.0</code> option
to <code>make-database</code>.</p>
<p>The ViewVC configuration files and template language have changed
dramatically. See the file <code>docs/upgrading-howto.html</code>
in the release for information on porting existing versions of
those items for use with ViewVC 1.1.0.</p>
</div> <!-- h2 -->
<div class="h2">
<h2 id="compatibility">Features and Fixes</h2>
<div class="h3">
<h3 id="">Extensible path-based authorization w/ Subversion authz support</h3>
<p>In a nutshell, ViewVC is now able to do path-based authorization.
ViewVC 1.0 has a configuration option for naming 'forbidden'
modules, but it is really limited &mdash; it basically just makes a
universal decision about which top-level directories in every
hosted repository should be hidden by ViewVC. People want
more, and specifically requested that ViewVC learn how to honor
Subversion's authz files and semantics. So, along with some other
types of authorization approaches, that's what ViewVC 1.1 can now
do. If you are using mod_authz_svn with Apache today, or
svnserve's built-in authorization support, then you can now point
ViewVC to the same authz configuration file and have it honor the
access rules you've defined for your repositories.</p>
<p>Note that ViewVC does <strong>not</strong> handle authentication,
though. You'll need to configure your web server to demand login
credentials from users, which the web server itself can then hand
off to ViewVC for application against the authorization rules
you've defined.</p>
<p class="warning">WARNING: The root listing view does not consult the
authorization subsystem when deciding what roots to display to a
given user. If you need to protect your root names, consider
disabling it by removing <code>roots</code> from the set of views
listed in the <code>allowed_views</code> configuration option.
<strong>UPDATE: This was fixed in ViewVC 1.1.3.</strong></p>
<p class="warning">WARNING: Support for path-based authorization is
incomplete in the experimental version control backend modules,
including the one that permits display of remote Subversion
repositories. <strong>UPDATE: This was fixed in ViewVC
1.1.15.</strong></p>
</div> <!-- h3 -->
<div class="h3">
<h3 id="">Subversion versioned properties display</h3>
<p>ViewVC 1.1 displays the properties that Subversion lets you store
on files and directories
(<code>svn:mime-type</code>, <code>svn:mergeinfo</code>,
<code>svn:ignore</code>, etc.). Directory properties are shown by
default at the bottom of that directory's entry listing. File
properties are displayed at the bottom of that file's
markup/annotate view.</p>
</div> <!-- h3 -->
<div class="h3">
<h3 id="">Unified markup and annotation views</h3>
<p>The "markup" and "annotate" views in ViewVC now have a unified look
and feel (driven by a single EZT template). Both views support
syntax highlighting and Subversion file property display.</p>
</div> <!-- h3 -->
<div class="h3">
<h3 id="">Unified, hassle-free Pygments-based syntax highlighting</h3>
<p>ViewVC 1.0 does syntax highlighting by working with GNU enscript, or
highlight, or php, or py2html &mdash; all these external tools just
to accomplish a single task. But they all do things in slightly
different ways. And if you configure them wrongly, you get strange
errors. <a href="http://www.pygments.org/">Pygments</a> (which is
also used by <a href="http://trac.edgewall.org/">Trac</a> for
syntax highlighting) is a Python package that requires no
configuration, is easier to use inside ViewVC, and so on. So
ViewVC 1.1 drops support for all those various old integrations,
and just uses Pygments for everything now. This change was about
developer and administrator sanity. There will be complaints, to
be sure, about how various color schemes differ and what file types
now are and aren't understood by the syntax highlighting engine,
but this change should vastly simplify the discussions of such
things.</p>
</div> <!-- h3 -->
<div class="h3">
<h3 id="">Better MIME detection and handling</h3>
<p>ViewVC typically consults a MIME types file to determine what kind
of file a given document is, based on its filename extension
(<code>.jpg</code> = <code>image/jpeg</code>, &hellip;). But
Subversion lets you dictate a file's MIME type using
the <code>svn:mime-type</code> property. ViewVC now recognizes and
honors that property as the preferred source of a file's MIME type.
This can be disabled in the configuration, though, which might be
desirable if many of your Subversion-versioned files carry the
generic <code>application/octet-stream</code> MIME type that
Subversion uses by default for non-human-readable files.</p>
<p>Also, ViewVC now allows you to specify multiple MIME type mapping
files that you'd like it to consult when determine the MIME type of
files based on their extensions. This allows administrators to
easily define their own custom mappings for ViewVC's benefit
without potentially affecting the mappings used by other site
services.</p>
</div> <!-- h3 -->
<div class="h3">
<h3 id="">Support for full content diffs</h3>
<p>ViewVC 1.1 expands the previously existing options of "colored
diff" and "long colored diff" with a new "full colored diff", which
shows the full contents of the changed file (instead of only the 3
or 15 lines of context shown via the older diff display types).</p>
</div> <!-- h3 -->
<div class="h3">
<h3 id="">Support for per-root configuration overrides</h3>
<p>In ViewVC 1.1, you can setup configuration option overrides on a
per-root (per-repository) basis (if you need/care to do so). See
the comments in the <code>viewvc.conf.dist</code> file for more on
how to do this.</p>
</div> <!-- h3 -->
<div class="h3">
<h3 id="">Optional email address obfuscation/mangling</h3>
<p>ViewVC can, when displaying revision metadata, munge strings that
look like email addresses to protect them from screen-scraping
spammers. For example, a log message that says, "Patch by:
cmpilato@red-bean.com" can optionally be displayed by ViewVC using
HTML entity encoding for the characters (a trick that causes no
visible change to the output, but that might confuse
unsophisticated spam bot crawlers) or as "Patch by: cmpilato@..."
(which isn't a complete email address at all, but might be enough
information for the human reading the log message to know who to
blame for the patch).</p>
</div> <!-- h3 -->
<div class="h3">
<h3 id="">Pagination improvements</h3>
<p>The way that ViewVC splits directory and log views across pages has
been reworked. The old way was "Fetch all the information you can
find, then display only one page's worth." The new way is "Fetch
only what you need to display the page requested, plus a little bit
of border information." This provides a large performance
enhancement for the default sort orderings.</p>
</div> <!-- h3 -->
</div> <!-- h2 -->
</body>
</html>

View File

@@ -0,0 +1,49 @@
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
<html>
<head>
<title>ViewVC: 1.2.0 Release Notes</title>
<style>
.h2, .h3 {
padding: 0.25em 0em;
background: white;
}
.warning {
font-style: italic;
}
</style>
</head>
<body>
<h1>ViewVC 1.2.0 Release Notes</h1>
<div class="h2">
<h2 id="introduction">Introduction</h2>
<p>ViewVC 1.2.0 is the superset of all previous ViewVC releases.</p>
</div> <!-- h2 -->
<div class="h2">
<h2 id="compatibility">Compatibility</h2>
<p>Each ViewVC release strives to maintain URL stability with previous
releases, and 1.2.0 is no exception. All URLs considered valid for
previous ViewVC releases should continue to work correctly in this
release, though possibly only via the use of HTTP redirects
(generated by ViewVC itself).</p>
</div> <!-- h2 -->
<div class="h2">
<h2 id="compatibility">Features and Fixes</h2>
<div class="h3">
<h3 id=""></h3>
</div> <!-- h3 -->
</div> <!-- h2 -->
</body>
</html>

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>ViewVC 1.0 Template Authoring Guide</title>
<title>ViewVC 1.2 Template Authoring Guide</title>
<style>
body {
background-color: rgb(180,193,205);
@@ -29,22 +29,26 @@ td {
.varlevel1 { background: rgb(65%,85%,65%); }
.varlevel2 { background: rgb(70%,90%,70%); }
.varlevel3 { background: rgb(75%,95%,75%); }
.varlevel4 { background: rgb(80%,100%,80%); }
.varlevel5 { background: rgb(85%,100%,85%); }
.varname { font-family: monospace; }
.varlevel1 .varname { padding-left: 0; }
.varlevel2 .varname { padding-left: 2em; }
.varlevel3 .varname { padding-left: 4em; }
.varlevel4 .varname { padding-left: 6em; }
.varlevel5 .varname { padding-left: 8em; }
.toc-list { font-size: 90%; }
</style>
</head>
<body>
<h1>ViewVC 1.0 Template Authoring Guide</h1>
<h1>ViewVC 1.2 Template Authoring Guide</h1>
<div class="h2">
<h2 id="introduction">Introduction</h2>
<p>This document represents an (unfinished) attempt at providing
documentation for how to customize ViewVC 1.0-dev's HTML output via
instructions for how to customize ViewVC's HTML output via
modification of its templates.</p>
</div>
@@ -161,12 +165,6 @@ td {
resource. Valid only when <var>pathtype</var> is <tt>file</tt>
or (for Subversion roots) <tt>dir</tt>.</td>
</tr>
<tr class="varlevel1">
<td class="varname">log_rev_href</td>
<td>String</td>
<td>Revision number of the file-revision currently being viewed, or
None.</td>
</tr>
<tr class="varlevel1">
<td class="varname">nav_path</td>
<td>List</td>
@@ -321,7 +319,7 @@ td {
<tr class="varlevel1">
<td class="varname">pathrev_hidden_values</td>
<td>List</td>
<td>Hidden value name/value pairs for the revision/tag selection form.</td>
<td>Hidden field name/value pairs for the revision/tag selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">pathrev_clear_action</td>
@@ -331,7 +329,7 @@ td {
<tr class="varlevel1">
<td class="varname">pathrev_clear_hidden_values</td>
<td>List</td>
<td>Hidden value name/value pairs for the path revision clear button.</td>
<td>Hidden field name/value pairs for the path revision clear button.</td>
</tr>
</tbody>
</table>
@@ -450,10 +448,10 @@ td {
<tr class="varlevel1">
<td class="varname">annotation</td>
<td>String</td>
<td>If set, indicates that annotations were requested. Valid values
are "annotated" (annotation was successful), "binary" (file contents
are not line-based and human-readable), and "error" (something went
wrong during annotation).</td>
<td>Valid values are "none" (no annotations were attempted),
"annotated" (annotation was successful), "binary" (file contents
are not line-based and human-readable), and "error" (something
went wrong during annotation).</td>
</tr>
<tr class="varlevel1">
<td class="varname">author</td>
@@ -604,6 +602,45 @@ td {
<td colspan="3">Includes all variables from the
<a href="#variables-common">COMMON</a> variable set</td>
</tr>
<tr class="varlevel1">
<td class="varname">gbbox</td>
<td>Boolean</td>
<td>Toggle generation of a branch box at the tip of all branches in
the revision graph.</td>
</tr>
<tr class="varlevel1">
<td class="varname">gflip</td>
<td>Boolean</td>
<td>Toggle the direction of the revision graph.</td>
</tr>
<tr class="varlevel1">
<td class="varname">gleft</td>
<td>Boolean</td>
<td>Toggle the orientation of the revision graph.</td>
</tr>
<tr class="varlevel1">
<td class="varname">gmaxtag</td>
<td>String</td>
<td>Number of tags per revision to display in the revision graph.</td>
</tr>
<tr class="varlevel1">
<td class="varname">graph_action</td>
<td>String</td>
<td>Form action URL for the graph customization form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">graph_hidden_values</td>
<td>String</td>
<td>Hidden value name/value pairs for the graph customization form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">gshow</td>
<td>String</td>
<td>Classes of revisions to show in the revision graph. Valid values
are <tt>all</tt> (all revision), <tt>inittagged</tt> (initial
revision(s) and tagged revisions), and <tt>tagged</tt> (tagged
revisions only).</td>
</tr>
<tr class="varlevel1">
<td class="varname">imagemap</td>
<td>String</td>
@@ -616,6 +653,37 @@ td {
<td>URL of the ViewVC revision graph image for the current
resource.</td>
</tr>
<tr class="varlevel1">
<td class="varname">opt_gbbox</td>
<td>Boolean</td>
<td>Specifies whether the user is allowed to toggle the generation
of branch boxes at the tip of all branches in the revision
graph.</td>
</tr>
<tr class="varlevel1">
<td class="varname">opt_gflip</td>
<td>Boolean</td>
<td>Specifies whether the user is allowed to toggle the direction
of the revision graph.</td>
</tr>
<tr class="varlevel1">
<td class="varname">opt_gleft</td>
<td>Boolean</td>
<td>Specifies whether the user is allowed to toggle the orientation
of the revision graph.</td>
</tr>
<tr class="varlevel1">
<td class="varname">opt_gmaxtag</td>
<td>Boolean</td>
<td>Specifies whether the user is allowed to configure the maximum
number of tags per revision show in the revision graph.</td>
</tr>
<tr class="varlevel1">
<td class="varname">opt_gshow</td>
<td>Boolean</td>
<td>Specifies whether the user is allowed to configure which
classes of revisions are shown in the revision graph.</td>
</tr>
</tbody>
</table>
</div>
@@ -636,30 +704,64 @@ td {
<a href="#variables-common">COMMON</a> variable set</td>
</tr>
<tr class="varlevel1">
<td class="varname">changes</td>
<td class="varname">diffs</td>
<td>List</td>
<td>Set of objects which contain information about a single line of
file difference data. Valid only when <var>diff_format</var> is
<tt>h</tt> or <tt>l</tt>.</td>
<td>List of all blocks of differences between the two sides, including content
and property differences.</td>
</tr>
<tr class="varlevel2">
<td class="varname">changes.have_left</td>
<td>Boolean</td>
<td>Specifies whether the left file has a line of content relevant
to the difference data line. Valid only when
<var>changes.type</var> is <tt>change</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">changes.have_right</td>
<td>Boolean</td>
<td>Specifies whether the right file has a line of content relevant
to the difference data line. Valid only when
<var>changes.type</var> is <tt>change</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">changes.left</td>
<td class="varname">diffs.diff_block_format</td>
<td>String</td>
<td>Textual contents of the relevant line in the left file. Valid
<td>Indicates the type of this block. One of the <tt>anchor</tt> (no display,
create an anchor), <tt>raw</tt> (non-colored diff, display as produced),
<tt>sidebyside-1</tt> (traditional side-by-side diff),
<tt>sidebyside-2</tt> (newer side-by-side diff with intraline changes),
<tt>unified</tt> (colored unified diff).</td>
</tr>
<tr class="varlevel2">
<td class="varname">diffs.anchor</td>
<td>String</td>
<td>If <var>diffs.diff_block_format</var> is <tt>anchor</tt>, this variable specifies
the anchor name.</td>
</tr>
<tr class="varlevel2">
<td class="varname">diffs.changes</td>
<td>List/Container</td>
<td>Set of objects which contain information about a change in a single
object (file or property). Not present if <var>diffs.diff_block_format</var> is
<tt>anchor</tt>, otherwise has different format depending on
<var>diffs.diff_block_format</var> (applicable as indicated in brackets below).</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.changes.raw</td>
<td>String</td>
<td>[raw] Diff text. Valid only if <var>diffs.changes.type</var> is
<tt>raw</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.changes.type</td>
<td>String</td>
<td>[raw] The type of change. Values: <tt>binary-diff</tt>,
<tt>error</tt>, <tt>no-changes</tt>, <tt>raw</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.changes.have_left</td>
<td>Boolean</td>
<td>[sidebyside-1] Specifies whether the left file has a line of content relevant
to the difference data line. Valid only when
<var>changes.type</var> is <tt>change</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.changes.have_right</td>
<td>Boolean</td>
<td>[sidebyside-1] Specifies whether the right file has a line of content relevant
to the difference data line. Valid only when
<var>changes.type</var> is <tt>change</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.changes.left</td>
<td>String</td>
<td>[sidebyside-1] Textual contents of the relevant line in the left file. Valid
only when <var>changes.type</var> is <tt>change</tt>,
<tt>context</tt>, or <tt>remove</tt>. When
<var>changes.type</var> is <tt>change</tt>, valid only when
@@ -667,10 +769,10 @@ td {
between missing lines and empty lines, which EZT does not
support).</td>
</tr>
<tr class="varlevel2">
<td class="varname">changes.right</td>
<tr class="varlevel3">
<td class="varname">diffs.changes.right</td>
<td>String</td>
<td>Textual contents of the relevant line in the right file. Valid
<td>[sidebyside-1] Textual contents of the relevant line in the right file. Valid
only when <var>changes.type</var> is <tt>add</tt>, <tt>change</tt>,
or <tt>context</tt>. When
<var>changes.type</var> is <tt>change</tt>, valid only when
@@ -678,40 +780,264 @@ td {
between missing lines and empty lines, which EZT does not
support).</td>
</tr>
<tr class="varlevel2">
<td class="varname">changes.line_info_extra</td>
<tr class="varlevel3">
<td class="varname">diffs.changes.line_info_extra</td>
<td>String</td>
<td>Additional line information for the current difference hunk.
<td>[sidebyside-1] Additional line information for the current difference hunk.
Valid only when <var>changes.type</var> is <tt>header</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">changes.line_info_left</td>
<tr class="varlevel3">
<td class="varname">diffs.changes.line_info_left</td>
<td>String</td>
<td>First line number represented by the current hunk in the left
<td>[sidebyside-1] First line number represented by the current hunk in the left
file. Valid only when <var>changes.type</var> is <tt>header</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">changes.line_info_right</td>
<tr class="varlevel3">
<td class="varname">diffs.changes.line_info_right</td>
<td>String</td>
<td>First line number represented by the current hunk in the right
<td>[sidebyside-1] First line number represented by the current hunk in the right
file. Valid only when <var>changes.type</var> is <tt>header</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">changes.line_number</td>
<tr class="varlevel3">
<td class="varname">diffs.changes.line_number</td>
<td>String</td>
<td>Line number (1-based) of the line.</td>
<td>[sidebyside-1] Line number (1-based) of the line.</td>
</tr>
<tr class="varlevel2">
<td class="varname">changes.type</td>
<tr class="varlevel3">
<td class="varname">diffs.changes.type</td>
<td>String</td>
<td>The type of change. Value values: <tt>add</tt>,
<tt>change</tt>, <tt>context</tt>, <tt>header</tt>,
<td>[sidebyside-1] The type of change. Values: <tt>add</tt>, <tt>binary-diff</tt>,
<tt>change</tt>, <tt>context</tt>, <tt>error</tt>, <tt>header</tt>,
<tt>no-changes</tt>, <tt>remove</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.changes.columns</td>
<td>List</td>
<td>[sidebyside-2] List of two columns for left and right parts of the diff.</td>
</tr>
<tr class="varlevel4">
<td class="varname">diffs.changes.columns.line_number</td>
<td>String</td>
<td>[sidebyside-2] Line number in the left/right column.</td>
</tr>
<tr class="varlevel4">
<td class="varname">diffs.changes.columns.segments</td>
<td>List</td>
<td>[sidebyside-2] Left/right line, broken into change segments.</td>
</tr>
<tr class="varlevel5">
<td class="varname">diffs.changes.columns.segments.text</td>
<td>String</td>
<td>[sidebyside-2] Text of this segment.</td>
</tr>
<tr class="varlevel5">
<td class="varname">diffs.changes.columns.segments.type</td>
<td>String</td>
<td>[sidebyside-2] Not set if the segment is the same in both left and right sides;
otherwise, one of the <tt>add</tt>, <tt>remove</tt> or <tt>change</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.changes.gap</td>
<td>Boolean</td>
<td>[sidebyside-2] If true, indicates that change blocks are non-contiguous
and that the template should display some sort of ellipsis before the
current block.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.changes.type</td>
<td>String</td>
<td>[sidebyside-2] The type of change. Values: <tt>binary-diff</tt>,
<tt>error</tt>, <tt>intraline</tt>, <tt>no-changes</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.changes.segments</td>
<td>List</td>
<td>[unified] Left/right line, broken into change segments.</td>
</tr>
<tr class="varlevel4">
<td class="varname">diffs.changes.segments.text</td>
<td>String</td>
<td>[unified] Text of this segment.</td>
</tr>
<tr class="varlevel4">
<td class="varname">diffs.changes.segments.type</td>
<td>String</td>
<td>[unified] Not set if the segment is the same in both left and right sides;
otherwise, one of the <tt>add</tt>, <tt>remove</tt> or <tt>change</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.changes.type</td>
<td>String</td>
<td>[unified] The type of change. Values: <tt>add</tt>, <tt>binary-diff</tt>,
<tt>error</tt>, <tt>no-changes</tt>, <tt>remove</tt> or empty string
if the line was not changed (context line).</td>
</tr>
<tr class="varlevel2">
<td class="varname">diffs.left</td>
<td>Container</td>
<td>Container object for grouping information about the left file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.ago</td>
<td>String</td>
<td>Text description of the time elapsed since <var>left.date</date>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.annotate_href</td>
<td>String</td>
<td>URL of the ViewVC annotation view for the left file.
Valid only when <var>entries.pathtype</var> is <tt>file</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.author</td>
<td>String</td>
<td>Author of the revision of the left file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.date</td>
<td>String</td>
<td>Date (in UTC if not otherwise configured) in which the left file
revision was created.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.download_href</td>
<td>String</td>
<td>URL to download the HEAD revision of the left file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.download_text_href</td>
<td>String</td>
<td>URL to download the HEAD revision of the left file as
<tt>text/plain</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.log</td>
<td>String</td>
<td>Log message of the left file revision.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.path</td>
<td>String</td>
<td>Path of the left file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.prefer_markup</td>
<td>Boolean</td>
<td>Indicates whether to make the default file link a link to the markup
page instead of the checkout page.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.rev</td>
<td>String</td>
<td>Revision of the left file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.revision_href</td>
<td>String</td>
<td>URL of the Subversion revision view for the left file's
current revision. Valid only when <var>roottype</var> is
<tt>svn</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.size</td>
<td>String</td>
<td>Size of the left file revision, in bytes. Subversion only.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.tag</td>
<td>String</td>
<td>Tag of the left file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.left.view_href</td>
<td>String</td>
<td>This is a URL for the markup view of the left file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">diffs.right</td>
<td>Container</td>
<td>Container object for grouping information about the right file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.ago</td>
<td>String</td>
<td>Text description of the time elapsed since <var>right.date</var>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.annotate_href</td>
<td>String</td>
<td>URL of the ViewVC annotation view for the right file.
Valid only when <var>entries.pathtype</var> is <tt>file</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.author</td>
<td>String</td>
<td>Author of the revision of the right file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.date</td>
<td>String</td>
<td>Date (in UTC if not otherwise configured) in which the right file
revision was created.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.download_href</td>
<td>String</td>
<td>URL to download the HEAD revision of the right file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.download_text_href</td>
<td>String</td>
<td>URL to download the HEAD revision of the right file as
<tt>text/plain</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.log</td>
<td>String</td>
<td>Log message of the right file revision.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.path</td>
<td>String</td>
<td>Path of the right file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.prefer_markup</td>
<td>Boolean</td>
<td>Indicates whether to make the default file link a link to the markup
page instead of the checkout page.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.rev</td>
<td>String</td>
<td>Revision of the right file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.revision_href</td>
<td>String</td>
<td>URL of the Subversion revision view for the right file's
current revision. Valid only when <var>roottype</var> is
<tt>svn</tt>.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.size</td>
<td>String</td>
<td>Size of the right file revision, in bytes. Subversion only.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.tag</td>
<td>String</td>
<td>Tag of the right file.</td>
</tr>
<tr class="varlevel3">
<td class="varname">diffs.right.view_href</td>
<td>String</td>
<td>This is a URL for the markup view of the right file.</td>
</tr>
<tr class="varlevel1">
<td class="varname">diff_format</td>
<td>String</td>
<td>Difference dislay format: Valid values are <tt>c</tt>
<td>Difference display format: Valid values are <tt>c</tt>
(context), <tt>f</tt> (full human-readable),
<tt>h</tt> (human-readable, or colored), <tt>l</tt> (long
human-readable), <tt>s</tt> (side-by-side), <tt>u</tt>
@@ -725,135 +1051,17 @@ td {
<tr class="varlevel1">
<td class="varname">diff_format_hidden_values</td>
<td>List</td>
<td>Hidden value name/value pairs for the diff format selection form.</td>
<td>Hidden field name/value pairs for the diff format selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">left</td>
<td>Container</td>
<td>Container object for grouping information about the left file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.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.date</td>
<td>String</td>
<td>Date (in UTC if not otherwise configured) in which the left file
revision was created.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.download_href</td>
<td>String</td>
<td>URL to download the HEAD revision of the left file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.download_text_href</td>
<td>String</td>
<td>URL to download the HEAD revision of the left file as
<tt>text/plain</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.path</td>
<td>String</td>
<td>Path of the left file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.prefer_markup</td>
<td class="varname">hide_legend</td>
<td>Boolean</td>
<td>Indicates whether to make the default file link a link to the markup
page instead of the checkout page.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.rev</td>
<td>String</td>
<td>Revision of the left file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.revision_href</td>
<td>String</td>
<td>URL of the Subversion revision view for the left file's
current revision. Valid only when <var>roottype</var> is
<tt>svn</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.tag</td>
<td>String</td>
<td>Tag of the left file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">left.view_href</td>
<td>String</td>
<td>This is a URL for the markup view of the left file.</td>
<td>Indicates whether the display format requires displaying a legend</td>
</tr>
<tr class="varlevel1">
<td class="varname">raw_diff</td>
<td class="varname">patch_href</td>
<td>String</td>
<td>Raw difference text. Valid only when <var>diff_format</var> is
<tt>c</tt>, <tt>s</tt>, or <tt>u</tt>.</td>
</tr>
<tr class="varlevel1">
<td class="varname">right</td>
<td>Container</td>
<td>Container object for grouping information about the right file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.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.date</td>
<td>String</td>
<td>Date (in UTC if not otherwise configured) in which the right file
revision was created.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.download_href</td>
<td>String</td>
<td>URL to download the HEAD revision of the right file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.download_text_href</td>
<td>String</td>
<td>URL to download the HEAD revision of the right file as
<tt>text/plain</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.path</td>
<td>String</td>
<td>Path of the right file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.prefer_markup</td>
<td>Boolean</td>
<td>Indicates whether to make the default file link a link to the markup
page instead of the checkout page.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.rev</td>
<td>String</td>
<td>Revision of the right file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.revision_href</td>
<td>String</td>
<td>URL of the Subversion revision view for the right file's
current revision. Valid only when <var>roottype</var> is
<tt>svn</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.tag</td>
<td>String</td>
<td>Tag of the right file.</td>
</tr>
<tr class="varlevel2">
<td class="varname">right.view_href</td>
<td>String</td>
<td>This is a URL for the markup view of the right file.</td>
<td>URL of the patch view for the file.</td>
</tr>
</tbody>
</table>
@@ -905,7 +1113,7 @@ td {
<tr class="varlevel1">
<td class="varname">dir_paging_hidden_values</td>
<td>List</td>
<td>Hidden value name/value pairs for the page selection form.</td>
<td>Hidden field name/value pairs for the page selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">entries</td>
@@ -1087,23 +1295,16 @@ td {
<td>String</td>
<td>Current search expression, if any.</td>
</tr>
<tr class="varlevel1">
<td class="varname">search_re_form</td>
<td>Boolean</td>
<td>Indicates whether or not to display the regular expression search
form. Value depends on the whether searching is enabled in the
configuration and whether or not the current directory is
empty.</td>
</tr>
<tr class="varlevel1">
<td class="varname">search_re_action</td>
<td>String</td>
<td>Form action URL for the regular expression search form.</td>
<td>Form action URL for the regular expression search form,
if searching is available.</td>
</tr>
<tr class="varlevel1">
<td class="varname">search_re_hidden_values</td>
<td>List</td>
<td>Hidden value name/value pairs for the regular expression search form.</td>
<td>Hidden field name/value pairs for the regular expression search form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">show_attic_href</td>
@@ -1249,7 +1450,7 @@ td {
<tr class="varlevel1">
<td class="varname">diff_select_hidden_values</td>
<td>List</td>
<td>Hidden value name/value pairs for the diff selection form.</td>
<td>Hidden field name/value pairs for the diff selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">entries</td>
@@ -1530,7 +1731,7 @@ td {
<tr class="varlevel1">
<td class="varname">log_paging_hidden_values</td>
<td>List</td>
<td>Hidden value name/value pairs for the page selection form.</td>
<td>Hidden field name/value pairs for the page selection form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">logsort</td>
@@ -1546,7 +1747,7 @@ td {
<tr class="varlevel1">
<td class="varname">logsort_hidden_values</td>
<td>List</td>
<td>Hidden value name/value pairs for the log sort drop down box</td>
<td>Hidden field name/value pairs for the log sort drop down box</td>
</tr>
<tr class="varlevel1">
<td class="varname">mime_type</td>
@@ -1794,6 +1995,14 @@ td {
<td>Indicates how query results are being sorted. Possible values:
<tt>date</tt>, <tt>author</tt>, and <tt>file</tt>.</td>
</tr>
<tr class="varlevel1">
<td class="varname">row_limit_reached</td>
<td>Boolean</td>
<td>Indicates whether the internal database row limit threshold (set
via the <code>cvsdb.row_limit</code>
and <code>cvsdb.rss_row_limit</code> configuration options) was
reached by the query.</td>
</tr>
<tr class="varlevel1">
<td class="varname">show_branch</td>
<td>Boolean</td>
@@ -1910,7 +2119,7 @@ td {
<tr class="varlevel1">
<td class="varname">query_hidden_values</td>
<td>List</td>
<td>Hidden value name/value pairs for query form.</td>
<td>Hidden field name/value pairs for query form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">querysort</td>
@@ -2050,7 +2259,7 @@ td {
<tr class="varlevel1">
<td class="varname">jump_rev_hidden_values</td>
<td>List</td>
<td>Hidden value name/value pairs for revision jump form.</td>
<td>Hidden field name/value pairs for revision jump form.</td>
</tr>
<tr class="varlevel1">
<td class="varname">limit_changes</td>
@@ -2073,6 +2282,11 @@ td {
<td>String</td>
<td>URL for the current view but with <tt>limit_changes</tt> disabled.</td>
</tr>
<tr class="varlevel1">
<td class="varname">num_changes</td>
<td>String</td>
<td>Number of paths changed in this revision.</td>
</tr>
<tr class="varlevel1">
<td class="varname">next_href</td>
<td>String</td>
@@ -2112,6 +2326,38 @@ td {
<td>List</td>
<td>Set of configured viewable repositories.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.ago</td>
<td>String</td>
<td>Textual description of the time since <var>roots.date</var>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.author</td>
<td>String</td>
<td>Username of the last modifier of the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">root.date</td>
<td>String</td>
<td>Date (in UTC if not otherwise configured) of the last
modification of the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.href</td>
<td>String</td>
<td>URL of root directory view for a configured repository.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.log</td>
<td>String</td>
<td>Log message of last modification to the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.log_href</td>
<td>String</td>
<td>URL of log revision view for the top-most (root) directory of
the root (repository).</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.name</td>
<td>String</td>
@@ -2125,17 +2371,24 @@ td {
configuration can have negative security implications. Use this
token at your own risk.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.rev</td>
<td>String</td>
<td>Youngest revision of the root.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.short_log</td>
<td>String</td>
<td>Log message of last modification to the root, truncated to
contain no more than the number of characters specified by
the <code>short_log_len</code> configuration option.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.type</td>
<td>String</td>
<td>Version control type of a configured repository. Valid
values: <tt>cvs</tt>, <tt>svn</tt>.</td>
</tr>
<tr class="varlevel2">
<td class="varname">roots.href</td>
<td>String</td>
<td>URL of root directory view for a configured repository.</td>
</tr>
</tbody>
</table>

View File

@@ -28,12 +28,12 @@ td {
.h3 { border-width: 1px 0 0 0; }
.toc-list { font-size: 90%; }
.varname { font-family: monospace; }
.added { background: rgb(50%,75%,25%); }
.added { background: rgb(60%,90%,60%); }
.unchanged { background: rgb(75%,75%,75%); }
.renamed { background: rgb(75%,50%,75%); }
.changed { background: rgb(100%,100%,25%); }
.replaced { background: rgb(100%,75%,0%); }
.removed { background: rgb(100%,25%,25%); }
.renamed { background: rgb(80%,60%,80%); }
.changed { background: rgb(100%,100%,50%); }
.replaced { background: rgb(100%,80%,40%); }
.removed { background: rgb(100%,70%,70%); }
</style>
</head>
@@ -67,12 +67,21 @@ td {
<h2 id="toc">Table of Contents</h2>
<ul class="toc-list">
<li><a href="#introduction">Introduction</a></li>
<li><a href="#sec-from-1-1">Upgrading From ViewVC 1.1</a></li>
<li><a href="#sec-from-1-0">Upgrading From ViewVC 1.0</a></li>
<li><a href="#sec-from-0-9">Upgrading From ViewCVS 0.9</a></li>
<li><a href="#sec-from-0-8">Upgrading From ViewCVS 0.8</a></li>
</ul>
</div>
<div class="h2">
<h2 id="sec-from-1-0">Upgrading From ViewVC 1.1</h2>
<p>This section discusses how to upgrade ViewVC 1.1.x to ViewVC 1.2.x.</p>
</div>
<div class="h2">
<h2 id="sec-from-1-0">Upgrading From ViewVC 1.0</h2>
@@ -137,9 +146,8 @@ td {
configuration's "general" section.</li>
<li>Finally, ensure that that the new <code>authorizer</code>
option is set to either "forbidden" (which is the default) or
"forbiddenre", depending on which of those you were using in
ViewVC 1.0.x.</li>
option is set to either "forbidden" or "forbiddenre", depending
on which of those you were using in ViewVC 1.0.x.</li>
</ol>
@@ -215,10 +223,9 @@ td {
<li>options/py2html_path</li>
<li>options/use_enscript</li>
<li>options/use_highlight</li>
<li>options/use_pagesize</li>
<li>options/use_php</li>
<li>options/use_py2html</li>
<li>options/use_pygments</li>
<li>options/use_source_highlight</li>
</ul>
</div>
@@ -226,16 +233,23 @@ td {
<div class="h3">
<h3>Checkin Database</h3>
<p>In ViewVC 1.1, the <code>svndbadmin</code> program's "rebuild"
subcommand has had its purpose become more defined. It no longer
accepts a revision argument, and therefore can now only be used to
completely rebuild the entirety of the checkin database information
for a Subversion repository (instead of being able to only update
the information related to single Subversion revision). For
per-revision updating, use <code>svndbadmin update</code> and
<p>ViewVC 1.1 introduces to the <code>cvsdbadmin</code>
and <code>svndbadmin</code> tools a new "purge" operation, which
allows you to remove all the information related to a given root
from your checkins database (without disturbing the information
associated with other roots). Likewise, the "rebuild" command in
those tools now implies a "purge" followed by an update.</p>
<p>As a related change, the <code>svndbadmin</code> program's
"rebuild" subcommand has had its purpose become more defined. It
no longer accepts a revision argument, and therefore can now only
be used to completely rebuild the entirety of the checkin database
information for a Subversion repository (instead of being able to
only update the information related to single Subversion revision).
For per-revision updating, use <code>svndbadmin update</code> and
provide a revision (or revision range). And to get the previous
rebuild-a-revision effect, pass the new <code>--force</code>
option to <code>svndbadmin update</code>.</p>
rebuild-a-revision effect, pass the new <code>--force</code> option
to <code>svndbadmin update</code>.</p>
<p>In other words, where you once did this:</p>
@@ -249,6 +263,19 @@ td {
</pre>
</blockquote>
<p>To enhance the performance of the new "purge" operation, ViewVC 1.1
introduces some slight changes to the checkin database schema. If
you use the <code>make-database</code> tool to (re)create your
checkins database, it will by default employ the new database
schema. This should cause the database to be virtually unusable by
previous versions of ViewVC, and that's by design. If, however,
you need to (re)create your checkins database and you require
compatibility with previous versions of ViewVC or ViewCVS, simply
pass the "--version=1.0" option to the <code>make-database</code>
script. Note that your "purge" and "rebuild" operations could be
abysmally slow, though, as that version of the database schema is
not optimized for those operations.</p>
</div>
<div class="h3">
@@ -296,6 +323,22 @@ td {
<li>options/allow_tar</li>
</ul>
<p>ViewVC now honors the "svn:mime-type" property stored on
Subversion-versioned files as the primary source of MIME type
determination (before falling back to name-based MIME mappings and
such). However, this can negatively affect the viewability of
certain files &mdash; especially images &mdash; whose
"svn:mime-type" properties are set incorrectly, such as will happen
if Subversion itself merely determines that the file isn't
human-readable and uses the "application/octet-stream" MIME type to
record this determination. To make ViewVC <em>not</em> honor the
"svn:mime-type" property value, set the <code>svn_ignore_mimetype</code>
configuration option.</p>
<p>Speaking of MIME types, the option <code>mime_types_file</code> is
now <code>mime_types_files</code>, as it now carries multiple paths
to MIME mappings files, ordered by preference.</p>
<p>The <code>use_rcsparse</code> option was moved from the "general"
section to the "options" section.</p>
@@ -314,7 +357,7 @@ td {
all = viewvc.*
[all-options]
allow_tar = 1
allowed_views = annotate, diff, markup, tar
</pre>
</blockquote>
@@ -324,7 +367,7 @@ allow_tar = 1
all = viewvc.*
[vhost-all/options]
allow_tar = 1
allowed_views = annotate, diff, markup, tar
</pre>
</blockquote>
@@ -631,6 +674,11 @@ allow_tar = 1
<td>revision.ezt</td>
<td>now is an iterable list of objects with .name and .value attributes</td>
</tr>
<tr class="added">
<td class="varname">num_changes</td>
<td>revision.ezt</td>
<td>added</td>
</tr>
</tbody>
</table>
@@ -659,7 +707,7 @@ allow_tar = 1
<h3>Checkin Database</h3>
<p>ViewVC 1.0 reads and writes commit times in the MySQL database in
UTC time rather than local time. This can cause times displayed on
UTC time rather than local time. This can cause times displayed on
the query page to be a few hours off if an old database is being
used with a new version of ViewVC. The best way to fix this is to
rebuild the database with the new version of cvsdbadmin, but it
@@ -702,7 +750,8 @@ allow_tar = 1
<li>options/root_as_url_component</li>
<li>options/default_file_view</li>
<li>options/sort_group_dirs</li>
<li>options/use_pagesize</li>
<li>options/dir_pagesize</li>
<li>options/log_pagesize</li>
<li>options/limit_changes</li>
<li>options/use_localtime</li>
<li>options/cross_copies</li>
@@ -1563,13 +1612,8 @@ allow_tar = 1
<div class="h2">
<h2 id="sec-from-0-8">Upgrading From ViewCVS 0.8</h2>
<p>This section discusses how to upgrade ViewCVS 0.8 to version
0.9 or a later version of the software.</p>
<p>This section discusses how to upgrade ViewCVS 0.8 to ViewCVS 0.9.x.</p>
<p><strong>NOTE:</strong> these changes will bring you up to the
requirements of version 0.9. You must also follow the directions
for <a href="#sec-from-0-9">upgrading from 0.9</a>.</p>
<div class="h3">
<h3>Configuration Options</h3>
@@ -1579,92 +1623,50 @@ allow_tar = 1
options, then you will need to make corresponding changes in the
templates.</p>
<dl>
<dt>
Colors:
<strong>diff_heading</strong>,
<strong>diff_empty</strong>,
<strong>diff_remove</strong>,
<strong>diff_change</strong>,
<strong>diff_add</strong>,
and <strong>diff_dark_change</strong>
</dt>
<dd>
These options have been incorporated into the
<code>diff.ezt</code> template.
<dl>
<dt>Colors: <code>diff_heading</code>, <code>diff_empty</code>,
<code>diff_remove</code>, <code>diff_change</code>,
<code>diff_add</code>, and <code>diff_dark_change</code></dt>
<dd>These options have been incorporated into the
<code>diff.ezt</code> template.</dd>
<p></p>
</dd>
<dt><code>markup_log</code></dt>
<dd>This option has been incorporated into the
<code>markup.ezt</code> template.</dd>
<dt><strong>markup_log</strong></dt>
<dd>
This option has been incorporated into the
<code>markup.ezt</code> template.
<dt>Colors: <code>nav_header</code> and
<code>alt_background</code></dt>
<dd>These options have been incorporated into the
<code>header.ezt</code> template.</dd>
<p></p>
</dd>
<dt>Images: <code>back_icon</code>, <code>dir_icon</code>,
and <code>file_icon</code></dt>
<dd>These options have been incorporated into the
<code>directory.ezt</code>, <code>header.ezt</code>,
<code>log.ezt</code>, <code>log_table.ezt</code>, and
<code>query.ezt</code> templates.</dd>
<dt>Colors: <strong>nav_header</strong>
and <strong>alt_background</strong></dt>
<dd>
These options have been incorporated into the
<code>header.ezt</code> template.
<dt><code>use_java_script</code>
and <code>open_extern_window</code></dt>
<dd>The templates now use JavaScript in all applicable places, and
open external windows for most downloading and viewing of
files. If you wish to not use JavaScript and/or external
windows, then remove the feature(s) from the templates.</dd>
<p></p>
</dd>
<dt><code>show_author</code></dt>
<dd>Changing this option would be quite strange and rare. If you
do not want to show the author for the revisions, then you
should remove it from the various templates.</dd>
<dt>
Images:
<strong>back_icon</strong>,
<strong>dir_icon</strong>,
and <strong>file_icon</strong>
</dt>
<dd>
These options have been incorporated into the
<code>directory.ezt</code>, <code>header.ezt</code>,
<code>log.ezt</code>, <code>log_table.ezt</code>, and
<code>query.ezt</code> templates.
<dt><code>hide_non_readable</code></dt>
<dd>This option was never used, so it has been removed.</dd>
<p></p>
</dd>
<dt><code>flip_links_in_dirview</code></dt>
<dd>This option is no longer available. If you want the links in
your directory view flipped, then you may use the
<code>dir_alternate.ezt</code> template.</dd>
<dt><strong>use_java_script</strong>
and <strong>open_extern_window</strong></dt>
<dd>
The templates now use JavaScript in all applicable places,
and open external windows for most downloading and viewing
of files. If you wish to not use JavaScript and/or external
windows, then remove the feature(s) from the templates.
<p></p>
</dd>
<dt><strong>show_author</strong></dt>
<dd>
Changing this option would be quite strange and rare. If you
do not want to show the author for the revisions, then you
should remove it from the various templates.
<p></p>
</dd>
<dt><strong>hide_non_readable</strong></dt>
<dd>
This option was never used, so it has been removed.
<p></p>
</dd>
<dt><strong>flip_links_in_dirview</strong></dt>
<dd>
This option is no longer available. If you want the links in
your directory view flipped, then you may use the
<code>dir_alternate.ezt</code> template.
<p></p>
</dd>
</dl>
</dl>
</div>
@@ -1675,53 +1677,65 @@ allow_tar = 1
removed in 0.9. If you have custom templates that refer to these
variables, then you will need to modify your templates.</p>
<dl>
<dt><code>directory.ezt</code>: <var>headers</var></dt>
<dd>
The headers are now listed explicitly in the template,
rather than made available through a list.
<p></p>
</dd>
<dt>
<code>directory.ezt</code>:
<var>rows.cols</var>,
and <var>rows.span</var>
</dt>
<dd>
These variables were used in conjunction with the
<var>headers</var> variable to control the column
displays. This is now controlled explicitly within the
templates.
<p></p>
</dd>
<dt><code>directory.ezt</code>:
<var>rev_in_front</var></dt>
<dd>
This was used to indicate that revision links should
be used in the first column, rather than in their
standard place in the second column. Changing the
links should now be done in the template, rather than
according to this variable. You may want to look at
the <code>dir_alternate.ezt</code> template, which has
the revision in front.
<p></p>
</dd>
<dt><code>directory.ezt</code>:
<var>rows.attic</var>
and <var>rows.hide_attic_href</var></dt>
<dd>
These variable were used to manage the hide and
showing of the contents of the <code>Attic/</code>
subdirectory. Several new variables were introduced
which can be used to replace this functionality:
<var>show_attic_href</var>,
<var>hide_attic_href</var>, and <var>rows.state</var>.
<p></p>
</dd>
</dl>
<table>
<thead>
<tr>
<th>Variable</th>
<th>Location</th>
<th>Changes</th>
</tr>
</thead>
<tbody>
<tr class="removed">
<td class="varname">headers</td>
<td>directory.ezt</td>
<td>removed; headers are now listed explicitly in the template, rather
than made available through a list.</td>
</tr>
<tr class="removed">
<td class="varname">rows.cols</td>
<td>directory.ezt</td>
<td>removed; was used in conjunction with the <var>headers</var>
variable to control the column displays. This is now controlled
explicitly within the templates.</td>
</tr>
<tr class="removed">
<td class="varname">rows.span</td>
<td>directory.ezt</td>
<td>removed; was used in conjunction with the <var>headers</var>
variable to control the column displays. This is now controlled
explicitly within the templates.</td>
</tr>
<tr class="removed">
<td class="varname">rev_in_front</td>
<td>directory.ezt</td>
<td>removed; was used to indicate that revision links should be used in
the first column, rather than in their standard place in the
second column. Changing the links should now be done in the
template, rather than according to this variable. You may want
to look at the <code>dir_alternate.ezt</code> template, which
has the revision in front.</dd>
</tr>
<tr class="removed">
<td class="varname">rows.attic</td>
<td>directory.ezt</td>
<td>removed; used to manage the hide and showing of the
contents of the <code>Attic/</code> subdirectory. Several new
variables were introduced which can be used to replace this
functionality: <var>show_attic_href</var>,
<var>hide_attic_href</var>, and <var>rows.state</var>.</td>
</tr>
<tr class="removed">
<td class="varname">rows.hide_attic_href</td>
<td>directory.ezt</td>
<td>removed; used to manage the hide and showing of the
contents of the <code>Attic/</code> subdirectory. Several new
variables were introduced which can be used to replace this
functionality: <var>show_attic_href</var>,
<var>hide_attic_href</var>, and <var>rows.state</var>.</td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@@ -654,6 +654,35 @@ th.caption {
<td>depends</td>
<td><a href="#root-param"><code>root</code> parameter</a></td>
</tr>
<tr>
<td><code>gflip=<var>GFLIP</var></code></td>
<td>optional</td>
<td>"1" if the revisions in the graph should run
youngest-to-oldest; "0" for the reverse</td>
</tr>
<tr>
<td><code>gbbox=<var>GBBOX</var></code></td>
<td>optional</td>
<td>"1" if the revision graph should contain branch boxes at the
tip of each branch; "0" otherwise</td>
</tr>
<tr>
<td><code>gleft=<var>GLEFT</var></code></td>
<td>optional</td>
<td>"1" if the revision graph should be orientated left-to-right;
"0" otherwise</td>
</tr>
<tr>
<td><code>gmaxtag=<var>GMAXTAG</var></code></td>
<td>optional</td>
<td>maximum number of per-revision tags to show in the revision graph</td>
</tr>
<tr>
<td><code>gshow=<var>GSHOW</var></code></td>
<td>optional</td>
<td>"all", "inittagged", or "tagged" &mdash; user-selected classes
of revision to show in the graph</td>
</tr>
</table>
<h3 id="graphimg-view">Graph Image View</h3>
@@ -700,6 +729,35 @@ th.caption {
<td>depends</td>
<td><a href="#root-param"><code>root</code> parameter</a></td>
</tr>
<tr>
<td><code>gflip=<var>GFLIP</var></code></td>
<td>optional</td>
<td>"1" if the revisions in the graph should run
youngest-to-oldest; "0" for the reverse</td>
</tr>
<tr>
<td><code>gbbox=<var>GBBOX</var></code></td>
<td>optional</td>
<td>"1" if the revision graph should contain branch boxes at the
tip of each branch; "0" otherwise</td>
</tr>
<tr>
<td><code>gleft=<var>GLEFT</var></code></td>
<td>optional</td>
<td>"1" if the revision graph should be orientated left-to-right;
"0" otherwise</td>
</tr>
<tr>
<td><code>gmaxtag=<var>GMAXTAG</var></code></td>
<td>optional</td>
<td>maximum number of per-revision tags to show in the revision graph</td>
</tr>
<tr>
<td><code>gshow=<var>GSHOW</var></code></td>
<td>optional</td>
<td>"all", "inittagged", or "tagged" &mdash; user-selected classes
of revision to show in the graph</td>
</tr>
</table>
<h3 id="log-view">Log View</h3>
@@ -996,7 +1054,7 @@ th.caption {
<td>file query string</td>
</tr>
<tr>
<td><code>file_match=FILE_MATCH</code></td>
<td><code>file_match=<var>FILE_MATCH</var></code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of file match</td>
@@ -1007,7 +1065,7 @@ th.caption {
<td>author query string</td>
</tr>
<tr>
<td><code>who_match=WHO_MATCH</code></td>
<td><code>who_match=<var>WHO_MATCH</var></code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of author match</td>
@@ -1024,36 +1082,36 @@ th.caption {
of log message match</td>
</tr>
<tr>
<td><code>querysort=SORT</code></td>
<td><code>querysort=<var>SORT</var></code></td>
<td>optional</td>
<td>"date" "author" or "file" determining order of query results</td>
</tr>
<tr>
<td><code>date=DATE</code></td>
<td><code>date=<var>DATE</var></code></td>
<td>optional</td>
<td>"hours" "day" "week" "month" "all" or "explicit" to filter
query results by date</td>
</tr>
<tr>
<td><code>hours=HOURS</code></td>
<td><code>hours=<var>HOURS</var></code></td>
<td>optional</td>
<td>number of hours back to include results from when
<code><var>DATE</var></code> is "hours"</td>
</tr>
<tr>
<td><code>mindate=MINDATE</code></td>
<td><code>mindate=<var>MINDATE</var></code></td>
<td>optional</td>
<td>earliest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>maxdate=MAXDATE</code></td>
<td><code>maxdate=<var>MAXDATE</var></code></td>
<td>optional</td>
<td>latest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>limit_changes=LIMIT_CHANGES</code></td>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
<td>optional</td>
<td>maximum number of files to list per commit in query
results. Default is value of <code>limit_changes</code>
@@ -1113,7 +1171,7 @@ th.caption {
<td>branch query string</td>
</tr>
<tr>
<td><code>branch_match=BRANCH_MATCH</code></td>
<td><code>branch_match=<var>BRANCH_MATCH</var></code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of branch match</td>
@@ -1129,7 +1187,7 @@ th.caption {
<td>file query string</td>
</tr>
<tr>
<td><code>file_match=FILE_MATCH</code></td>
<td><code>file_match=<var>FILE_MATCH</var></code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of file match</td>
@@ -1140,7 +1198,7 @@ th.caption {
<td>author query string</td>
</tr>
<tr>
<td><code>who_match=WHO_MATCH</code></td>
<td><code>who_match=<var>WHO_MATCH</var></code></td>
<td>optional</td>
<td>"exact" "like" "glob" "regex" or "notregex" determining type
of author match</td>
@@ -1157,50 +1215,43 @@ th.caption {
of log message match</td>
</tr>
<tr>
<td><code>querysort=SORT</code></td>
<td><code>querysort=<var>SORT</var></code></td>
<td>optional</td>
<td>"date" "author" or "file" determining order of query results</td>
</tr>
<tr>
<td><code>date=DATE</code></td>
<td><code>date=<var>DATE</var></code></td>
<td>optional</td>
<td>"hours" "day" "week" "month" "all" or "explicit" to filter
query results by date</td>
</tr>
<tr>
<td><code>hours=HOURS</code></td>
<td><code>hours=<var>HOURS</var></code></td>
<td>optional</td>
<td>number of hours back to include results from when
<code><var>DATE</var></code> is "hours"</td>
</tr>
<tr>
<td><code>mindate=MINDATE</code></td>
<td><code>mindate=<var>MINDATE</var></code></td>
<td>optional</td>
<td>earliest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>maxdate=MAXDATE</code></td>
<td><code>maxdate=<var>MAXDATE</var></code></td>
<td>optional</td>
<td>latest date to include results from when
<code><var>DATE</var></code> is "explicit"</td>
</tr>
<tr>
<td><code>format=FORMAT</code></td>
<td><code>format=<var>FORMAT</var></code></td>
<td>optional</td>
<td>"rss" or "backout" values to generate an rss feed or list of
commands to back out changes instead showing a normal query result
page</td>
</tr>
<tr>
<td><code>limit=LIMIT</code></td>
<td>optional</td>
<td>maximum number of file-revisions to process during a
query. Default is value of <code>row_limit</code> configuration
option</td>
</tr>
<tr>
<td><code>limit_changes=LIMIT_CHANGES</code></td>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
<td>optional</td>
<td>maximum number of files to list per commit in query
results. Default is value of <code>limit_changes</code>
@@ -1254,7 +1305,7 @@ th.caption {
<td><a href="#revision-param"><code>revision</code> parameter</a></td>
</tr>
<tr>
<td><code>limit_changes=LIMIT_CHANGES</code></td>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
<td>optional</td>
<td>maximum number of files to list per commit. Default is value
of <code>limit_changes</code> configuration option</td>

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -15,7 +15,6 @@
# -----------------------------------------------------------------------
import re
import string
def language(hdr):
@@ -39,8 +38,8 @@ def _parse(hdr, result):
while pos < len(hdr):
name = _re_token.match(hdr, pos)
if not name:
raise AcceptParseError()
a = result.item_class(string.lower(name.group(1)))
raise AcceptLanguageParseError()
a = result.item_class(name.group(1).lower())
pos = name.end()
while 1:
# are we looking at a parameter?
@@ -56,7 +55,7 @@ def _parse(hdr, result):
# the "=" was probably missing
continue
pname = string.lower(match.group(1))
pname = match.group(1).lower()
if pname == 'q' or pname == 'qs':
try:
a.quality = float(match.group(2))
@@ -70,7 +69,7 @@ def _parse(hdr, result):
# bad float literal
pass
elif pname == 'charset':
a.charset = string.lower(match.group(2))
a.charset = match.group(2).lower()
result.append(a)
if hdr[pos:pos+1] == ',':
@@ -210,7 +209,7 @@ class _LanguageSelector:
def append(self, item):
self.requested.append(item)
class AcceptParseError(Exception):
class AcceptLanguageParseError(Exception):
pass
def _test():

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
#
# By using this file, you agree to the terms and conditions set forth in
@@ -27,14 +27,14 @@
# -----------------------------------------------------------------------
import sys
import string
import os
import re
import time
import math
import cgi
import vclib
from common import _item
import vclib
import sapi
re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"')
@@ -43,7 +43,7 @@ def link_includes(text, repos, path_parts, include_url):
if match:
incfile = match.group(3)
include_path_parts = path_parts[:-1]
for part in filter(None, string.split(incfile, '/')):
for part in filter(None, incfile.split('/')):
if part == "..":
if not include_path_parts:
# nothing left to pop; don't bother marking up this include.
@@ -55,14 +55,14 @@ def link_includes(text, repos, path_parts, include_url):
include_path = None
try:
if repos.itemtype(include_path_parts, None) == vclib.FILE:
include_path = string.join(include_path_parts, '/')
include_path = '/'.join(include_path_parts)
except vclib.ItemNotFound:
pass
if include_path:
return '#%sinclude%s<a href="%s">"%s"</a>' % \
(match.group(1), match.group(2),
string.replace(include_url, '/WHERE/', include_path), incfile)
include_url.replace('/WHERE/', include_path), incfile)
return text
@@ -75,14 +75,15 @@ class HTMLBlameSource:
self.path_parts = path_parts
self.diff_url = diff_url
self.include_url = include_url
self.annotation, self.revision = self.repos.annotate(path_parts, opt_rev)
self.annotation, self.revision = self.repos.annotate(path_parts, opt_rev,
True)
def __getitem__(self, idx):
item = self.annotation.__getitem__(idx)
diff_url = None
if item.prev_rev:
diff_url = '%sr1=%s&amp;r2=%s' % (self.diff_url, item.prev_rev, item.rev)
thisline = link_includes(cgi.escape(item.text), self.repos,
thisline = link_includes(sapi.escape(item.text), self.repos,
self.path_parts, self.include_url)
return _item(text=thisline, line_number=item.line_number,
rev=item.rev, prev_rev=item.prev_rev,
@@ -94,11 +95,6 @@ def blame(repos, path_parts, diff_url, include_url, opt_rev=None):
return source, source.revision
class _item:
def __init__(self, **kw):
vars(self).update(kw)
def make_html(root, rcs_path):
import vclib.ccvs.blame
bs = vclib.ccvs.blame.BlameSource(os.path.join(root, rcs_path))
@@ -136,7 +132,7 @@ def make_html(root, rcs_path):
sys.stdout.write('<td>&nbsp;</td><td>&nbsp;</td>')
rev_count = rev_count + 1
sys.stdout.write('<td%s>%s</td></tr>\n' % (align % 'left', string.rstrip(thisline) or '&nbsp;'))
sys.stdout.write('<td%s>%s</td></tr>\n' % (align % 'left', thisline.rstrip() or '&nbsp;'))
sys.stdout.write('</table>\n')

60
lib/common.py Normal file
View File

@@ -0,0 +1,60 @@
# -*-python-*-
#
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# common: common definitions for the viewvc library
#
# -----------------------------------------------------------------------
# Special type indicators for diff header processing and idiff return codes
_RCSDIFF_IS_BINARY = 'binary-diff'
_RCSDIFF_ERROR = 'error'
_RCSDIFF_NO_CHANGES = "no-changes"
class _item:
def __init__(self, **kw):
vars(self).update(kw)
class TemplateData:
"""A custom dictionary-like object that allows one-time definition
of keys, and only value fetches and changes, and key deletions,
thereafter.
EZT doesn't require the use of this special class -- a normal
dict-type data dictionary works fine. But use of this class will
assist those who want the data sent to their templates to have a
consistent set of keys."""
def __init__(self, initial_data={}):
self._items = initial_data
def __getitem__(self, key):
return self._items.__getitem__(key)
def __setitem__(self, key, item):
assert self._items.has_key(key)
return self._items.__setitem__(key, item)
def __delitem__(self, key):
return self._items.__delitem__(key)
def keys(self):
return self._items.keys()
def merge(self, template_data):
"""Merge the data in TemplataData instance TEMPLATA_DATA into this
instance. Avoid the temptation to use this conditionally in your
code -- it rather defeats the purpose of this class."""
assert isinstance(template_data, TemplateData)
self._items.update(template_data._items)

View File

@@ -1,180 +0,0 @@
# -*-python-*-
#
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
# distribution or at http://viewvc.org/license-1.html.
#
# For more information, visit http://viewvc.org/
#
# -----------------------------------------------------------------------
#
# compat.py: compatibility functions for operation across Python 1.5.x to 2.2.x
#
# -----------------------------------------------------------------------
import urllib
import string
import time
import calendar
import re
import os
import rfc822
import tempfile
import errno
#
# urllib.urlencode() is new to Python 1.5.2
#
try:
urlencode = urllib.urlencode
except AttributeError:
def urlencode(dict):
"Encode a dictionary as application/x-url-form-encoded."
if not dict:
return ''
quote = urllib.quote_plus
keyvalue = [ ]
for key, value in dict.items():
keyvalue.append(quote(key) + '=' + quote(str(value)))
return string.join(keyvalue, '&')
#
# time.strptime() is new to Python 1.5.2
#
if hasattr(time, 'strptime'):
def cvs_strptime(timestr):
'Parse a CVS-style date/time value.'
return time.strptime(timestr, '%Y/%m/%d %H:%M:%S')[:-1] + (0,)
else:
_re_rev_date = re.compile('([0-9]{4})/([0-9][0-9])/([0-9][0-9]) '
'([0-9][0-9]):([0-9][0-9]):([0-9][0-9])')
def cvs_strptime(timestr):
'Parse a CVS-style date/time value.'
match = _re_rev_date.match(timestr)
if match:
return tuple(map(int, match.groups())) + (0, 1, 0)
else:
raise ValueError('date is not in cvs format')
#
# os.makedirs() is new to Python 1.5.2
#
try:
makedirs = os.makedirs
except AttributeError:
def makedirs(path, mode=0777):
head, tail = os.path.split(path)
if head and tail and not os.path.exists(head):
makedirs(head, mode)
os.mkdir(path, mode)
#
# rfc822.formatdate() is new to Python 1.6
#
try:
formatdate = rfc822.formatdate
except AttributeError:
def formatdate(timeval):
if timeval is None:
timeval = time.time()
timeval = time.gmtime(timeval)
return "%s, %02d %s %04d %02d:%02d:%02d GMT" % (
["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"][timeval[6]],
timeval[2],
["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][timeval[1]-1],
timeval[0], timeval[3], timeval[4], timeval[5])
#
# calendar.timegm() is new to Python 2.x and
# calendar.leapdays() was wrong in Python 1.5.2
#
try:
timegm = calendar.timegm
except AttributeError:
def leapdays(year1, year2):
"""Return number of leap years in range [year1, year2).
Assume year1 <= year2."""
year1 = year1 - 1
year2 = year2 - 1
return (year2/4 - year1/4) - (year2/100 -
year1/100) + (year2/400 - year1/400)
EPOCH = 1970
def timegm(tuple):
"""Unrelated but handy function to calculate Unix timestamp from GMT."""
year, month, day, hour, minute, second = tuple[:6]
# assert year >= EPOCH
# assert 1 <= month <= 12
days = 365*(year-EPOCH) + leapdays(EPOCH, year)
for i in range(1, month):
days = days + calendar.mdays[i]
if month > 2 and calendar.isleap(year):
days = days + 1
days = days + day - 1
hours = days*24 + hour
minutes = hours*60 + minute
seconds = minutes*60 + second
return seconds
#
# tempfile.mkdtemp() is new to Python 2.3
#
try:
mkdtemp = tempfile.mkdtemp
except AttributeError:
def mkdtemp(suffix="", prefix="tmp", dir=None):
# mktemp() only took a single suffix argument until Python 2.3.
# We'll do the best we can.
oldtmpdir = os.environ.get('TMPDIR')
try:
for i in range(10):
if dir:
os.environ['TMPDIR'] = dir
dir = tempfile.mktemp(suffix)
if prefix:
parent, base = os.path.split(dir)
dir = os.path.join(parent, prefix + base)
try:
os.mkdir(dir, 0700)
return dir
except OSError, e:
if e.errno == errno.EEXIST:
continue # try again
raise
finally:
if oldtmpdir:
os.environ['TMPDIR'] = oldtmpdir
elif os.environ.has_key('TMPDIR'):
del(os.environ['TMPDIR'])
raise IOError, (errno.EEXIST, "No usable temporary directory name found")
#
# the following stuff is *ONLY* needed for standalone.py.
# For that reason I've encapsulated it into a function.
#
def for_standalone():
import SocketServer
if not hasattr(SocketServer.TCPServer, "close_request"):
#
# method close_request() was missing until Python 2.1
#
class TCPServer(SocketServer.TCPServer):
def process_request(self, request, client_address):
"""Call finish_request.
Overridden by ForkingMixIn and ThreadingMixIn.
"""
self.finish_request(request, client_address)
self.close_request(request)
def close_request(self, request):
"""Called to clean up an individual request."""
request.close()
SocketServer.TCPServer = TCPServer

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -16,7 +16,6 @@
import sys
import os
import string
import ConfigParser
import fnmatch
@@ -24,57 +23,163 @@ 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 |
# | |
# `-----------'
# ,-----------.
# | |
# | query |
# | |
# `-----------'
#
# ### 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')
_base_sections = (
# Base configuration sections.
'authz-*',
'cvsdb',
'general',
'options',
'query',
'templates',
'utilities',
)
_force_multi_value = (
# Configuration values with multiple, comma-separated values.
'allowed_views',
'binary_mime_types',
'custom_log_formatting',
'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.optionxform = lambda x: x # don't case-normalize option names.
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(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:
if fname[0] == '[':
idx = string.index(fname, ']')
parts = string.split(fname[1:idx], '.')
fname = string.strip(fname[idx+1:])
idx = fname.index(']')
parts = fname[1:idx].split('.')
fname = fname[idx+1:].strip()
else:
parts = [ ]
fname = string.replace(fname, '%lang%', language)
fname = fname.replace('%lang%', language)
parser = ConfigParser.ConfigParser()
parser.optionxform = lambda x: x # don't case-normalize option names.
parser.read(os.path.join(self.base, fname))
for section in parser.sections():
for option in parser.options(section):
@@ -92,75 +197,109 @@ 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):
value = parser.get(section, opt)
if opt in self._force_multi_value:
value = map(string.strip, filter(None, string.split(value, ',')))
value = map(lambda x: x.strip(), filter(None, value.split(',')))
else:
try:
value = int(value)
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, 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, 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(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('vhost', canon_vhost, section)
if base_section:
self._process_section(parser, section, base_section)
def _find_canon_vhost(self, parser, vhost):
vhost = string.split(string.lower(vhost), ':')[0] # lower-case, no port
vhost = vhost.lower().split(':')[0] # lower-case, no port
for canon_vhost in parser.options('vhosts'):
value = parser.get('vhosts', canon_vhost)
patterns = map(string.lower, map(string.strip,
filter(None, string.split(value, ','))))
patterns = map(lambda x: x.lower().strip(),
filter(None, value.split(',')))
for pat in patterns:
if fnmatch.fnmatchcase(vhost, pat):
return canon_vhost
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."""
did_overlay = 0
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('root', rootname, section)
if base_section:
# We can currently only deal with root overlays happening
# once, so check that we've not yet done any overlaying of
# per-root options.
assert(self.root_options_overlayed == 0)
self._process_section(self.parser, section, base_section)
did_overlay = 1
# If we actually did any overlaying, remember this fact so we
# don't do it again later.
if did_overlay:
self.root_options_overlayed = 1
def _get_parser_items(self, parser, section):
"""Basically implement ConfigParser.items() for pre-Python-2.3 versions."""
@@ -171,23 +310,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):
@@ -197,7 +380,7 @@ class Config:
self.general.svn_roots = { }
self.general.root_parents = []
self.general.default_root = ''
self.general.mime_types_file = ''
self.general.mime_types_files = ["mimetypes.conf"]
self.general.address = ''
self.general.kv_files = [ ]
self.general.languages = ['en-us']
@@ -207,19 +390,22 @@ class Config:
self.utilities.cvsnt = 'cvs'
else:
self.utilities.cvsnt = None
self.utilities.svn = ''
self.utilities.diff = ''
self.utilities.cvsgraph = ''
self.options.root_as_url_component = 1
self.options.checkout_magic = 0
self.options.allowed_views = ['markup', 'annotate', 'roots']
self.options.authorizer = 'forbidden'
self.options.allowed_views = ['annotate', 'diff', 'markup', 'roots']
self.options.authorizer = None
self.options.mangle_email_addresses = 0
self.options.custom_log_formatting = []
self.options.default_file_view = "log"
self.options.binary_mime_types = []
self.options.http_expiration_time = 600
self.options.generate_etags = 1
self.options.svn_ignore_mimetype = 0
self.options.svn_config_dir = None
self.options.max_filesize_kbytes = 512
self.options.use_rcsparse = 0
self.options.sort_by = 'file'
self.options.sort_group_dirs = 1
@@ -234,19 +420,26 @@ class Config:
self.options.hr_ignore_keyword_subst = 1
self.options.hr_intraline = 0
self.options.allow_compress = 0
self.options.template_dir = "templates"
self.options.template_dir = "templates/default"
self.options.docroot = None
self.options.show_subdir_lastmod = 0
self.options.show_roots_lastmod = 0
self.options.show_logs = 1
self.options.show_log_in_markup = 1
self.options.cross_copies = 0
self.options.cross_copies = 1
self.options.use_localtime = 0
self.options.iso8601_timestamps = 0
self.options.short_log_len = 80
self.options.enable_syntax_coloration = 1
self.options.tabsize = 8
self.options.detect_encoding = 0
self.options.use_cvsgraph = 0
self.options.cvsgraph_conf = "cvsgraph.conf"
self.options.allowed_cvsgraph_useropts = []
self.options.use_re_search = 0
self.options.use_pagesize = 0
self.options.dir_pagesize = 0
self.options.log_pagesize = 0
self.options.log_pagesextra = 3
self.options.limit_changes = 100
self.templates.diff = None
@@ -272,17 +465,19 @@ class Config:
self.cvsdb.rss_row_limit = 100
self.cvsdb.check_database_for_root = 0
self.query.viewvc_base_url = None
def _startswith(somestr, substr):
return somestr[:len(substr)] == substr
def _parse_roots(config_name, config_value):
roots = { }
for root in config_value:
pos = string.find(root, ':')
if pos < 0:
try:
name, path = root.split(':', 1)
except:
raise MalformedRoot(config_name, root)
name, path = map(string.strip, (root[:pos], root[pos+1:]))
roots[name] = path
roots[name.strip()] = path.strip()
return roots
class ViewVCConfigurationError(Exception):
@@ -308,10 +503,3 @@ class MalformedRoot(ViewVCConfigurationError):
class _sub_config:
pass
if not hasattr(sys, 'hexversion'):
# Python 1.5 or 1.5.1. fix the syntax for ConfigParser options.
import regex
ConfigParser.option_cre = regex.compile('^\([-A-Za-z0-9._]+\)\(:\|['
+ string.whitespace
+ ']*=\)\(.*\)$')

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -12,7 +12,6 @@
import os
import sys
import string
import time
import fnmatch
import re
@@ -20,6 +19,14 @@ import re
import vclib
import dbi
## Current commits database schema version number.
##
## Version 0 was the original Bonsai-compatible version.
##
## Version 1 added the 'metadata' table (which holds the 'version' key)
## and renamed all the 'repository'-related stuff to be 'root'-
##
CURRENT_SCHEMA_VERSION = 1
## error
error = "cvsdb error"
@@ -30,13 +37,13 @@ error = "cvsdb error"
## complient database interface
class CheckinDatabase:
def __init__(self, host, port, user, passwd, database, row_limit):
def __init__(self, host, port, user, passwd, database):
self._host = host
self._port = port
self._user = user
self._passwd = passwd
self._database = database
self._row_limit = row_limit
self._version = None
## database lookup caches
self._get_cache = {}
@@ -48,6 +55,19 @@ class CheckinDatabase:
self._host, self._port, self._user, self._passwd, self._database)
cursor = self.db.cursor()
cursor.execute("SET AUTOCOMMIT=1")
table_list = self.GetTableList()
if 'metadata' in table_list:
version = self.GetMetadataValue("version")
if version is None:
self._version = 0
else:
self._version = int(version)
else:
self._version = 0
if self._version > CURRENT_SCHEMA_VERSION:
raise DatabaseVersionError("Database version %d is newer than the "
"last version supported by this "
"software." % (self._version))
def sql_get_id(self, table, column, value, auto_set):
sql = "SELECT id FROM %s WHERE %s=%%s" % (table, column)
@@ -147,6 +167,45 @@ class CheckinDatabase:
return list
def GetCommitsTable(self):
return self._version >= 1 and 'commits' or 'checkins'
def GetTableList(self):
sql = "SHOW TABLES"
cursor = self.db.cursor()
cursor.execute(sql)
list = []
while 1:
row = cursor.fetchone()
if row == None:
break
list.append(row[0])
return list
def GetMetadataValue(self, name):
sql = "SELECT value FROM metadata WHERE name=%s"
sql_args = (name)
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
try:
(value,) = cursor.fetchone()
except TypeError:
return None
return value
def SetMetadataValue(self, name, value):
assert(self._version > 0)
sql = "REPLACE INTO metadata (name, value) VALUES (%s, %s)"
sql_args = (name, value)
cursor = self.db.cursor()
try:
cursor.execute(sql, sql_args)
except Exception, e:
raise Exception("Error setting metadata: '%s'\n"
"\tname = %s\n"
"\tvalue = %s\n"
% (str(e), name, value))
def GetBranchID(self, branch, auto_set = 1):
return self.get_id("branches", "branch", branch, auto_set)
@@ -251,7 +310,8 @@ class CheckinDatabase:
minus_count = commit.GetMinusCount() or '0'
description_id = self.GetDescriptionID(commit.GetDescription())
sql = "REPLACE INTO checkins"\
sql = "REPLACE INTO %s" % (self.GetCommitsTable())
sql = sql + \
" (type,ci_when,whoid,repositoryid,dirid,fileid,revision,"\
" stickytag,branchid,addedlines,removedlines,descid)"\
"VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
@@ -291,9 +351,17 @@ class CheckinDatabase:
match = " LIKE "
elif query_entry.match == "glob":
match = " REGEXP "
# use fnmatch to translate the glob into a regexp
# Use fnmatch to translate the glob into a regular
# expression. Sadly, we have to account for the fact
# that in Python 2.6, fnmatch.translate() started
# sticking '\Z(?ms)' at the end of the regular
# expression instead of just '$', and doesn't prepend
# the '^'.
data = fnmatch.translate(data)
if data[0] != '^': data = '^' + data
if data[0] != '^':
data = '^' + data
if data[-7:] == '\Z(?ms)':
data = data[:-7] + '$'
elif query_entry.match == "regex":
match = " REGEXP "
elif query_entry.match == "notregex":
@@ -301,61 +369,72 @@ class CheckinDatabase:
sqlList.append("%s%s%s" % (field, match, self.db.literal(data)))
return "(%s)" % (string.join(sqlList, " OR "))
return "(%s)" % (" OR ".join(sqlList))
def CreateSQLQueryString(self, query):
tableList = [("checkins", None)]
def CreateSQLQueryString(self, query, detect_leftover=0):
commits_table = self.GetCommitsTable()
tableList = [(commits_table, None)]
condList = []
if len(query.repository_list):
tableList.append(("repositories",
"(checkins.repositoryid=repositories.id)"))
"(%s.repositoryid=repositories.id)"
% (commits_table)))
temp = self.SQLQueryListString("repositories.repository",
query.repository_list)
condList.append(temp)
if len(query.branch_list):
tableList.append(("branches", "(checkins.branchid=branches.id)"))
tableList.append(("branches",
"(%s.branchid=branches.id)" % (commits_table)))
temp = self.SQLQueryListString("branches.branch",
query.branch_list)
condList.append(temp)
if len(query.directory_list):
tableList.append(("dirs", "(checkins.dirid=dirs.id)"))
tableList.append(("dirs",
"(%s.dirid=dirs.id)" % (commits_table)))
temp = self.SQLQueryListString("dirs.dir", query.directory_list)
condList.append(temp)
if len(query.file_list):
tableList.append(("files", "(checkins.fileid=files.id)"))
tableList.append(("files",
"(%s.fileid=files.id)" % (commits_table)))
temp = self.SQLQueryListString("files.file", query.file_list)
condList.append(temp)
if len(query.author_list):
tableList.append(("people", "(checkins.whoid=people.id)"))
tableList.append(("people",
"(%s.whoid=people.id)" % (commits_table)))
temp = self.SQLQueryListString("people.who", query.author_list)
condList.append(temp)
if len(query.comment_list):
tableList.append(("descs", "(checkins.descid=descs.id)"))
tableList.append(("descs",
"(%s.descid=descs.id)" % (commits_table)))
temp = self.SQLQueryListString("descs.description",
query.comment_list)
condList.append(temp)
if query.from_date:
temp = "(checkins.ci_when>=\"%s\")" % (str(query.from_date))
temp = "(%s.ci_when>=\"%s\")" \
% (commits_table, str(query.from_date))
condList.append(temp)
if query.to_date:
temp = "(checkins.ci_when<=\"%s\")" % (str(query.to_date))
temp = "(%s.ci_when<=\"%s\")" \
% (commits_table, str(query.to_date))
condList.append(temp)
if query.sort == "date":
order_by = "ORDER BY checkins.ci_when DESC,descid"
order_by = "ORDER BY %s.ci_when DESC,descid" % (commits_table)
elif query.sort == "author":
tableList.append(("people", "(checkins.whoid=people.id)"))
tableList.append(("people",
"(%s.whoid=people.id)" % (commits_table)))
order_by = "ORDER BY people.who,descid"
elif query.sort == "file":
tableList.append(("files", "(checkins.fileid=files.id)"))
tableList.append(("files",
"(%s.fileid=files.id)" % (commits_table)))
order_by = "ORDER BY files.file,descid"
## exclude duplicates from the table list, and split out join
@@ -369,32 +448,39 @@ class CheckinDatabase:
tables.append(table)
if cond is not None: joinConds.append(cond)
tables = string.join(tables, ",")
conditions = string.join(joinConds + condList, " AND ")
tables = ",".join(tables)
conditions = " AND ".join(joinConds + condList)
conditions = conditions and "WHERE %s" % conditions
## limit the number of rows requested or we could really slam
## a server with a large database
## apply the query's row limit, if any (so we avoid really
## slamming a server with a large database)
limit = ""
if query.limit:
limit = "LIMIT %s" % (str(query.limit))
elif self._row_limit:
limit = "LIMIT %s" % (str(self._row_limit))
if detect_leftover:
limit = "LIMIT %s" % (str(query.limit + 1))
else:
limit = "LIMIT %s" % (str(query.limit))
sql = "SELECT checkins.* FROM %s %s %s %s" % (
tables, conditions, order_by, limit)
sql = "SELECT %s.* FROM %s %s %s %s" \
% (commits_table, tables, conditions, order_by, limit)
return sql
def RunQuery(self, query):
sql = self.CreateSQLQueryString(query)
sql = self.CreateSQLQueryString(query, 1)
cursor = self.db.cursor()
cursor.execute(sql)
query.SetExecuted()
row_count = 0
while 1:
row = cursor.fetchone()
if not row:
break
row_count = row_count + 1
if query.limit and (row_count > query.limit):
query.SetLimitReached()
break
(dbType, dbCI_When, dbAuthorID, dbRepositoryID, dbDirID,
dbFileID, dbRevision, dbStickyTag, dbBranchID, dbAddedLines,
@@ -433,8 +519,15 @@ class CheckinDatabase:
if file_id == None:
return None
sql = "SELECT * FROM checkins WHERE "\
" repositoryid=%s AND dirid=%s AND fileid=%s AND revision=%s"
sql = "SELECT type, ci_when, whoid, repositoryid, dirid, fileid, " \
"revision, stickytag, branchid, addedlines, removedlines, " \
"descid "\
" FROM %s WHERE "\
" repositoryid=%%s "\
" AND dirid=%%s"\
" AND fileid=%%s"\
" AND revision=%%s"\
% (self.GetCommitsTable())
sql_args = (repository_id, dir_id, file_id, commit.GetRevision())
cursor = self.db.cursor()
@@ -452,39 +545,73 @@ class CheckinDatabase:
sql = "DELETE FROM %s WHERE %s=%%s" % (table, key)
sql_args = (value, )
if keep_fkey:
sql += " AND %s NOT IN (SELECT %s FROM checkins WHERE %s = %%s)" \
% (key, keep_fkey, keep_fkey)
sql_args = (value, value)
sql += " AND %s NOT IN (SELECT %s FROM %s WHERE %s = %%s)" \
% (key, keep_fkey, self.GetCommitsTable(), keep_fkey)
sql_args = (value, value)
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
def PurgeRepository(self, repository):
rep_id = self.GetRepositoryID(repository)
if not rep_id:
raise Exception, "Unknown repository '%s'" % (repository)
sql = "SELECT * FROM checkins WHERE repositoryid=%s"
sql_args = (rep_id, )
def sql_purge(self, table, key, fkey, ftable):
sql = "DELETE FROM %s WHERE %s NOT IN (SELECT %s FROM %s)" \
% (table, key, fkey, ftable)
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
checkins = []
while 1:
try:
(ci_type, ci_when, who_id, repository_id,
dir_id, file_id, revision, sticky_tag, branch_id,
plus_count, minus_count, description_id) = cursor.fetchone()
except TypeError:
break
checkins.append([file_id, dir_id, branch_id, description_id, who_id])
cursor.execute(sql)
def PurgeRepository(self, repository):
rep_id = self.GetRepositoryID(repository, auto_set=0)
if not rep_id:
raise UnknownRepositoryError("Unknown repository '%s'"
% (repository))
if (self._version >= 1):
self.sql_delete('repositories', 'id', rep_id)
self.sql_purge('commits', 'repositoryid', 'id', 'repositories')
self.sql_purge('files', 'id', 'fileid', 'commits')
self.sql_purge('dirs', 'id', 'dirid', 'commits')
self.sql_purge('branches', 'id', 'branchid', 'commits')
self.sql_purge('descs', 'id', 'descid', 'commits')
self.sql_purge('people', 'id', 'whoid', 'commits')
else:
sql = "SELECT type, ci_when, whoid, repositoryid, dirid, " \
"fileid, revision, stickytag, branchid, addedlines, " \
"removedlines, descid "\
" FROM checkins WHERE repositoryid=%s"
sql_args = (rep_id, )
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
checkins = []
while 1:
try:
(ci_type, ci_when, who_id, repository_id,
dir_id, file_id, revision, sticky_tag, branch_id,
plus_count, minus_count, description_id) = \
cursor.fetchone()
except TypeError:
break
checkins.append([file_id, dir_id, branch_id,
description_id, who_id])
#self.sql_delete('repositories', 'id', rep_id)
self.sql_delete('checkins', 'repositoryid', rep_id)
for checkin in checkins:
self.sql_delete('files', 'id', checkin[0], 'fileid')
self.sql_delete('dirs', 'id', checkin[1], 'dirid')
self.sql_delete('branches', 'id', checkin[2], 'branchid')
self.sql_delete('descs', 'id', checkin[3], 'descid')
self.sql_delete('people', 'id', checkin[4], 'whoid')
# Reset all internal id caches. We could be choosier here,
# but let's just be as safe as possible.
self._get_cache = {}
self._get_id_cache = {}
self._desc_id_cache = {}
class DatabaseVersionError(Exception):
pass
class UnknownRepositoryError(Exception):
pass
#self.sql_delete('repositories', 'id', rep_id)
self.sql_delete('checkins', 'repositoryid', rep_id)
for checkin in checkins:
self.sql_delete('files', 'id', checkin[0], 'fileid')
self.sql_delete('dirs', 'id', checkin[1], 'dirid')
self.sql_delete('branches', 'id', checkin[2], 'branchid')
self.sql_delete('descs', 'id', checkin[3], 'descid')
self.sql_delete('people', 'id', checkin[4], 'whoid')
## the Commit class holds data on one commit, the representation is as
## close as possible to how it should be committed and retrieved to the
@@ -661,8 +788,9 @@ class QueryEntry:
self.data = data
self.match = match
## CheckinDatabaseQueryData is a object which contains the search parameters
## for a query to the CheckinDatabase
## CheckinDatabaseQuery is an object which contains the search
## parameters for a query to the Checkin Database and -- after the
## query is executed -- the data returned by the query.
class CheckinDatabaseQuery:
def __init__(self):
## sorting
@@ -682,7 +810,8 @@ class CheckinDatabaseQuery:
## limit on number of rows to return
self.limit = None
self.limit_reached = 0
## list of commits -- filled in by CVS query
self.commit_list = []
@@ -690,6 +819,9 @@ class CheckinDatabaseQuery:
## are added
self.commit_cb = None
## has this query been run?
self.executed = 0
def SetRepository(self, repository, match = "exact"):
self.repository_list.append(QueryEntry(repository, match))
@@ -735,6 +867,20 @@ class CheckinDatabaseQuery:
def AddCommit(self, commit):
self.commit_list.append(commit)
def SetExecuted(self):
self.executed = 1
def SetLimitReached(self):
self.limit_reached = 1
def GetLimitReached(self):
assert self.executed
return self.limit_reached
def GetCommitList(self):
assert self.executed
return self.commit_list
##
## entrypoints
@@ -753,7 +899,7 @@ def ConnectDatabase(cfg, readonly=0):
user = cfg.cvsdb.user
passwd = cfg.cvsdb.passwd
db = CheckinDatabase(cfg.cvsdb.host, cfg.cvsdb.port, user, passwd,
cfg.cvsdb.database_name, cfg.cvsdb.row_limit)
cfg.cvsdb.database_name)
db.Connect()
return db
@@ -763,7 +909,7 @@ def ConnectDatabaseReadOnly(cfg):
def GetCommitListFromRCSFile(repository, path_parts, revision=None):
commit_list = []
directory = string.join(path_parts[:-1], "/")
directory = "/".join(path_parts[:-1])
file = path_parts[-1]
revs = repository.itemlog(path_parts, revision, vclib.SORTBY_DEFAULT,
@@ -780,7 +926,7 @@ def GetCommitListFromRCSFile(repository, path_parts, revision=None):
if rev.changed:
# extract the plus/minus and drop the sign
plus, minus = string.split(rev.changed)
plus, minus = rev.changed.split()
commit.SetPlusCount(plus[1:])
commit.SetMinusCount(minus[1:])

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -14,7 +14,7 @@ import sys
import time
import types
import re
import compat
import calendar
import MySQLdb
# set to 1 to store commit times in UTC, or 0 to use the ViewVC machine's
@@ -55,7 +55,7 @@ def TicksFromDateTime(datetime):
t = datetime.tuple()
if utc_time:
return compat.timegm(t)
return calendar.timegm(t)
else:
return time.mktime(t[:8] + (-1,))

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -48,13 +48,17 @@ if SHOW_TIMES:
else:
_times[which] = t
def dump():
for name, value in _times.items():
print '%s: %.6f<br />' % (name, value)
def t_dump(out):
out.write('<div>')
names = _times.keys()
names.sort()
for name in names:
out.write('%s: %.6fs<br/>\n' % (name, _times[name]))
out.write('</div>')
else:
t_start = t_end = dump = lambda *args: None
t_start = t_end = t_dump = lambda *args: None
class ViewVCException:

View File

@@ -347,7 +347,7 @@ class Template:
for_names = [ ]
if base_format:
program.append((self._cmd_format, _printers[base_format]))
program.append((self._cmd_format, _formatters[base_format]))
for i in range(len(parts)):
piece = parts[i]
@@ -405,13 +405,13 @@ class Template:
elif cmd == 'format':
if args[1][0]:
# argument is a variable reference
printer = args[1]
formatter = args[1]
else:
# argument is a string constant referring to built-in printer
printer = _printers.get(args[1][1])
if not printer:
# argument is a string constant referring to built-in formatter
formatter = _formatters.get(args[1][1])
if not formatter:
raise UnknownFormatConstantError(str(args[1:]))
program.append((self._cmd_format, printer))
program.append((self._cmd_format, formatter))
# remember the cmd, current pos, args, and a section placeholder
stack.append([cmd, len(program), args[1:], None])
@@ -460,15 +460,18 @@ class Template:
def _cmd_print(self, valrefs, ctx):
value = _get_value(valrefs[0], ctx)
args = map(lambda valref, ctx=ctx: _get_value(valref, ctx), valrefs[1:])
_write_value(value, args, ctx)
try:
_write_value(value, args, ctx)
except TypeError:
raise Exception("Unprintable value type for '%s'" % (str(valrefs[0][0])))
def _cmd_format(self, printer, ctx):
if type(printer) is TupleType:
printer = _get_value(printer, ctx)
ctx.printers.append(printer)
def _cmd_format(self, formatter, ctx):
if type(formatter) is TupleType:
formatter = _get_value(formatter, ctx)
ctx.formatters.append(formatter)
def _cmd_end_format(self, valref, ctx):
ctx.printers.pop()
ctx.formatters.pop()
def _cmd_include(self, (valref, reader), ctx):
fname = _get_value(valref, ctx)
@@ -522,7 +525,8 @@ class Template:
((valref,), unused, section) = args
list = _get_value(valref, ctx)
if isinstance(list, StringType):
raise NeedSequenceError()
raise NeedSequenceError("The value of '%s' is not a sequence"
% (valref[0]))
refname = valref[0]
ctx.for_iterators[refname] = iterator = _iter(list)
for unused in iterator:
@@ -633,14 +637,23 @@ def _get_value((refname, start, rest), ctx):
# string or a sequence
return ob
def _print_formatted(formatters, ctx, chunk):
# print chunk to ctx.fp after running it sequentially through formatters
for formatter in formatters:
chunk = formatter(chunk)
ctx.fp.write(chunk)
def _write_value(value, args, ctx):
# value is a callback function, generates its own output
if callable(value):
apply(value, [ctx] + list(args))
return
# pop printer in case it recursively calls _write_value
printer = ctx.printers.pop()
# squirrel away formatters in case one of them recursively calls
# _write_value() -- we'll use them (in reverse order) to format our
# output.
formatters = ctx.formatters[:]
formatters.reverse()
try:
# if the value has a 'read' attribute, then it is a stream: copy it
@@ -649,7 +662,7 @@ def _write_value(value, args, ctx):
chunk = value.read(16384)
if not chunk:
break
printer(ctx, chunk)
_print_formatted(formatters, ctx, chunk)
# value is a substitution pattern
elif args:
@@ -662,21 +675,23 @@ def _write_value(value, args, ctx):
piece = args[idx]
else:
piece = '<undef>'
printer(ctx, piece)
_print_formatted(formatters, ctx, piece)
# plain old value, write to output
else:
printer(ctx, value)
_print_formatted(formatters, ctx, value)
finally:
ctx.printers.append(printer)
# restore our formatters
formatters.reverse()
ctx.formatters = formatters
class Context:
"""A container for the execution context"""
def __init__(self, fp):
self.fp = fp
self.printers = []
self.formatters = []
def write(self, value, args=()):
_write_value(value, args, self)
@@ -789,20 +804,26 @@ class BaseUnavailableError(EZTException):
class UnknownFormatConstantError(EZTException):
"""The format specifier is an unknown value."""
def _raw_printer(ctx, s):
ctx.fp.write(s)
def _html_printer(ctx, s):
ctx.fp.write(cgi.escape(s))
def _raw_formatter(s):
return s
def _uri_printer(ctx, s):
ctx.fp.write(urllib.quote(s))
def _html_formatter(s):
return cgi.escape(s)
_printers = {
FORMAT_RAW : _raw_printer,
FORMAT_HTML : _html_printer,
FORMAT_XML : _html_printer,
FORMAT_URI : _uri_printer,
def _xml_formatter(s):
s = s.replace('&', '&#x26;')
s = s.replace('<', '&#x3C;')
s = s.replace('>', '&#x3E;')
return s
def _uri_formatter(s):
return urllib.quote(s)
_formatters = {
FORMAT_RAW : _raw_formatter,
FORMAT_HTML : _html_formatter,
FORMAT_XML : _xml_formatter,
FORMAT_URI : _uri_formatter,
}
# --- standard test environment ---

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -19,8 +19,10 @@ from __future__ import generators
import difflib
import sys
import re
from common import _item, _RCSDIFF_NO_CHANGES
import ezt
import cgi
import sapi
def sidebyside(fromlines, tolines, context):
"""Generate side by side diff"""
@@ -29,6 +31,7 @@ def sidebyside(fromlines, tolines, context):
line_strip = lambda line: line.rstrip("\n")
fromlines = map(line_strip, fromlines)
tolines = map(line_strip, tolines)
had_changes = 0
gap = False
for fromdata, todata, flag in difflib._mdiff(fromlines, tolines, context):
@@ -37,8 +40,11 @@ def sidebyside(fromlines, tolines, context):
else:
from_item = _mdiff_split(flag, fromdata)
to_item = _mdiff_split(flag, todata)
yield _item(gap=ezt.boolean(gap), columns=(from_item, to_item))
had_changes = 1
yield _item(gap=ezt.boolean(gap), columns=(from_item, to_item), type="intraline")
gap = False
if not had_changes:
yield _item(type=_RCSDIFF_NO_CHANGES)
_re_mdiff = re.compile("\0([+-^])(.*?)\1")
@@ -49,18 +55,18 @@ def _mdiff_split(flag, (line_number, text)):
while True:
m = _re_mdiff.search(text, pos)
if not m:
segments.append(_item(text=cgi.escape(text[pos:]), type=None))
segments.append(_item(text=sapi.escape(text[pos:]), type=None))
break
if m.start() > pos:
segments.append(_item(text=cgi.escape(text[pos:m.start()]), type=None))
segments.append(_item(text=sapi.escape(text[pos:m.start()]), type=None))
if m.group(1) == "+":
segments.append(_item(text=cgi.escape(m.group(2)), type="add"))
segments.append(_item(text=sapi.escape(m.group(2)), type="add"))
elif m.group(1) == "-":
segments.append(_item(text=cgi.escape(m.group(2)), type="remove"))
segments.append(_item(text=sapi.escape(m.group(2)), type="remove"))
elif m.group(1) == "^":
segments.append(_item(text=cgi.escape(m.group(2)), type="change"))
segments.append(_item(text=sapi.escape(m.group(2)), type="change"))
pos = m.end()
@@ -71,19 +77,26 @@ def unified(fromlines, tolines, context):
diff = difflib.Differ().compare(fromlines, tolines)
lastrow = None
had_changes = 0
for row in _trim_context(diff, context):
if row[0].startswith("? "):
had_changes = 1
yield _differ_split(lastrow, row[0])
lastrow = None
else:
if lastrow:
had_changes = 1
yield _differ_split(lastrow, None)
lastrow = row
if lastrow:
had_changes = 1
yield _differ_split(lastrow, None)
if not had_changes:
yield _item(type=_RCSDIFF_NO_CHANGES)
def _trim_context(lines, context_size):
"""Trim context lines that don't surround changes from Differ results
@@ -166,20 +179,16 @@ def _differ_split(row, guide):
for m in _re_differ.finditer(guide, pos):
if m.start() > pos:
segments.append(_item(text=cgi.escape(line[pos:m.start()]), type=None))
segments.append(_item(text=cgi.escape(line[m.start():m.end()]),
segments.append(_item(text=sapi.escape(line[pos:m.start()]), type=None))
segments.append(_item(text=sapi.escape(line[m.start():m.end()]),
type="change"))
pos = m.end()
segments.append(_item(text=cgi.escape(line[pos:]), type=None))
segments.append(_item(text=sapi.escape(line[pos:]), type=None))
return _item(gap=ezt.boolean(gap), type=type, segments=segments,
left_number=left_number, right_number=right_number)
class _item:
def __init__(self, **kw):
vars(self).update(kw)
try:
### Using difflib._mdiff function here was the easiest way of obtaining
### intraline diffs for use in ViewVC, but it doesn't exist prior to

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -23,7 +23,6 @@ import os
import sys
import sapi
import threading
import string
if sys.platform == "win32":
import win32popen
@@ -36,7 +35,7 @@ def popen(cmd, args, mode, capture_err=1):
if sys.platform == "win32":
command = win32popen.CommandLine(cmd, args)
if string.find(mode, 'r') >= 0:
if mode.find('r') >= 0:
hStdIn = None
if debug.SHOW_CHILD_PROCESSES:
@@ -85,7 +84,7 @@ def popen(cmd, args, mode, capture_err=1):
# in the parent
# close the descriptor that we don't need and return the other one.
if string.find(mode, 'r') >= 0:
if mode.find('r') >= 0:
os.close(w)
return _pipe(os.fdopen(r, mode), pid)
os.close(r)
@@ -96,7 +95,7 @@ def popen(cmd, args, mode, capture_err=1):
# we'll need /dev/null for the discarded I/O
null = os.open('/dev/null', os.O_RDWR)
if string.find(mode, 'r') >= 0:
if mode.find('r') >= 0:
# hook stdout/stderr to the "write" channel
os.dup2(w, 1)
# "close" stdin; the child shouldn't use it
@@ -125,200 +124,12 @@ def popen(cmd, args, mode, capture_err=1):
os.execvp(cmd, (cmd,) + tuple(args))
except:
# aid debugging, if the os.execvp above fails for some reason:
print "<h2>exec failed:</h2><pre>", cmd, string.join(args), "</pre>"
print "<h2>exec failed:</h2><pre>", cmd, ' '.join(args), "</pre>"
raise
# crap. shouldn't be here.
sys.exit(127)
def pipe_cmds(cmds, out=None):
"""Executes a sequence of commands. The output of each command is directed to
the input of the next command. A _pipe object is returned for writing to the
first command's input. The output of the last command is directed to the
"out" file object or the standard output if "out" is None. If "out" is not an
OS file descriptor, a separate thread will be spawned to send data to its
write() method."""
if out is None:
out = sys.stdout
if sys.platform == "win32":
### FIXME: windows implementation ignores "out" argument, always
### writing last command's output to standard out
if debug.SHOW_CHILD_PROCESSES:
dbgIn = StringIO.StringIO()
hStdIn, handle = win32popen.MakeSpyPipe(1, 0, (dbgIn,))
i = 0
for cmd in cmds:
i = i + 1
dbgOut, dbgErr = StringIO.StringIO(), StringIO.StringIO()
if i < len(cmds):
nextStdIn, hStdOut = win32popen.MakeSpyPipe(1, 1, (dbgOut,))
x, hStdErr = win32popen.MakeSpyPipe(None, 1, (dbgErr,))
else:
ehandle = win32event.CreateEvent(None, 1, 0, None)
nextStdIn, hStdOut = win32popen.MakeSpyPipe(None, 1, (dbgOut, sapi.server.file()), ehandle)
x, hStdErr = win32popen.MakeSpyPipe(None, 1, (dbgErr,))
command = win32popen.CommandLine(cmd[0], cmd[1:])
phandle, pid, thandle, tid = win32popen.CreateProcess(command, hStdIn, hStdOut, hStdErr)
if debug.SHOW_CHILD_PROCESSES:
debug.Process(command, dbgIn, dbgOut, dbgErr)
dbgIn = dbgOut
hStdIn = nextStdIn
else:
hStdIn, handle = win32popen.CreatePipe(1, 0)
spool = None
i = 0
for cmd in cmds:
i = i + 1
if i < len(cmds):
nextStdIn, hStdOut = win32popen.CreatePipe(1, 1)
else:
# very last process
nextStdIn = None
if sapi.server.inheritableOut:
# send child output to standard out
hStdOut = win32popen.MakeInheritedHandle(win32popen.FileObject2File(sys.stdout),0)
ehandle = None
else:
ehandle = win32event.CreateEvent(None, 1, 0, None)
x, hStdOut = win32popen.MakeSpyPipe(None, 1, (sapi.server.file(),), ehandle)
command = win32popen.CommandLine(cmd[0], cmd[1:])
phandle, pid, thandle, tid = win32popen.CreateProcess(command, hStdIn, hStdOut, None)
hStdIn = nextStdIn
return _pipe(win32popen.File2FileObject(handle, 'wb'), phandle, ehandle)
# flush the stdio buffers since we are about to change the FD under them
sys.stdout.flush()
sys.stderr.flush()
prev_r, parent_w = os.pipe()
null = os.open('/dev/null', os.O_RDWR)
child_pids = []
for cmd in cmds[:-1]:
r, w = os.pipe()
pid = os.fork()
if not pid:
# in the child
# hook up stdin to the "read" channel
os.dup2(prev_r, 0)
# hook up stdout to the output channel
os.dup2(w, 1)
# toss errors
os.dup2(null, 2)
# close these extra descriptors
os.close(prev_r)
os.close(parent_w)
os.close(null)
os.close(r)
os.close(w)
# time to run the command
try:
os.execvp(cmd[0], cmd)
except:
pass
sys.exit(127)
# in the parent
child_pids.append(pid)
# we don't need these any more
os.close(prev_r)
os.close(w)
# the read channel of this pipe will feed into to the next command
prev_r = r
# no longer needed
os.close(null)
# done with most of the commands. set up the last command to write to "out"
if not hasattr(out, 'fileno'):
r, w = os.pipe()
pid = os.fork()
if not pid:
# in the child (the last command)
# hook up stdin to the "read" channel
os.dup2(prev_r, 0)
# hook up stdout to "out"
if hasattr(out, 'fileno'):
if out.fileno() != 1:
os.dup2(out.fileno(), 1)
out.close()
else:
# "out" can't be hooked up directly, so use a pipe and a thread
os.dup2(w, 1)
os.close(r)
os.close(w)
# close these extra descriptors
os.close(prev_r)
os.close(parent_w)
# run the last command
try:
os.execvp(cmds[-1][0], cmds[-1])
except:
pass
sys.exit(127)
child_pids.append(pid)
# not needed any more
os.close(prev_r)
if not hasattr(out, 'fileno'):
os.close(w)
thread = _copy(r, out)
thread.start()
else:
thread = None
# write into the first pipe, wait on the final process
return _pipe(os.fdopen(parent_w, 'w'), child_pids, thread=thread)
class _copy(threading.Thread):
def __init__(self, srcfd, destfile):
self.srcfd = srcfd
self.destfile = destfile
threading.Thread.__init__(self)
def run(self):
try:
while 1:
s = os.read(self.srcfd, 1024)
if not s:
break
self.destfile.write(s)
finally:
os.close(self.srcfd)
class _pipe:
"Wrapper for a file which can wait() on a child process at close time."

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -23,6 +23,7 @@ import sys
import string
import time
from common import _item, TemplateData
import cvsdb
import viewvc
import ezt
@@ -47,7 +48,7 @@ class FormData:
def decode_thyself(self, form):
try:
self.repository = string.strip(form["repository"].value)
self.repository = form["repository"].value.strip()
except KeyError:
pass
except TypeError:
@@ -56,7 +57,7 @@ class FormData:
self.valid = 1
try:
self.branch = string.strip(form["branch"].value)
self.branch = form["branch"].value.strip()
except KeyError:
pass
except TypeError:
@@ -65,7 +66,7 @@ class FormData:
self.valid = 1
try:
self.directory = string.strip(form["directory"].value)
self.directory = form["directory"].value.strip()
except KeyError:
pass
except TypeError:
@@ -74,7 +75,7 @@ class FormData:
self.valid = 1
try:
self.file = string.strip(form["file"].value)
self.file = form["file"].value.strip()
except KeyError:
pass
except TypeError:
@@ -83,7 +84,7 @@ class FormData:
self.valid = 1
try:
self.who = string.strip(form["who"].value)
self.who = form["who"].value.strip()
except KeyError:
pass
except TypeError:
@@ -92,14 +93,14 @@ class FormData:
self.valid = 1
try:
self.sortby = string.strip(form["sortby"].value)
self.sortby = form["sortby"].value.strip()
except KeyError:
pass
except TypeError:
pass
try:
self.date = string.strip(form["date"].value)
self.date = form["date"].value.strip()
except KeyError:
pass
except TypeError:
@@ -158,7 +159,7 @@ def listparse_string(str):
## command; add the command and start over
elif c == ",":
## strip ending whitespace on un-quoted data
temp = string.rstrip(temp)
temp = temp.rstrip()
return_list.append( ("", temp) )
temp = ""
state = "eat leading whitespace"
@@ -217,8 +218,9 @@ def decode_command(cmd):
else:
return "exact"
def form_to_cvsdb_query(form_data):
def form_to_cvsdb_query(cfg, form_data):
query = cvsdb.CreateCheckinQuery()
query.SetLimit(cfg.cvsdb.row_limit)
if form_data.repository:
for cmd, str in listparse_string(form_data.repository):
@@ -266,18 +268,40 @@ def form_to_cvsdb_query(form_data):
def prev_rev(rev):
'''Returns a string representing the previous revision of the argument.'''
r = string.split(rev, '.')
r = rev.split('.')
# decrement final revision component
r[-1] = str(int(r[-1]) - 1)
# prune if we pass the beginning of the branch
if len(r) > 2 and r[-1] == '0':
r = r[:-2]
return string.join(r, '.')
return '.'.join(r)
def is_forbidden(cfg, cvsroot_name, module):
auth_params = cfg.get_authorizer_params('forbidden', cvsroot_name)
forbidden = auth_params.get('forbidden', '')
forbidden = map(string.strip, filter(None, string.split(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(lambda x: x.strip(), filter(None, forbidden.split(',')))
default = 0
for pat in forbidden:
if pat[0] == '!':
@@ -290,11 +314,7 @@ def is_forbidden(cfg, cvsroot_name, module):
def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
ob = _item(num_files=len(files), files=[])
if desc:
ob.log = string.replace(server.escape(desc), '\n', '<br />')
else:
ob.log = '&nbsp;'
ob.log = desc and server.escape(desc).replace('\n', '<br />') or ''
for commit in files:
repository = commit.GetRepository()
@@ -303,7 +323,7 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
## find the module name (if any)
try:
module = filter(None, string.split(directory, '/'))[0]
module = filter(None, directory.split('/'))[0]
except IndexError:
module = None
@@ -328,9 +348,10 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
except:
raise Exception, str([directory, commit.GetFile()])
## if we couldn't find the cvsroot path configured in the
## viewvc.conf file, then don't make the link
if cvsroot_name:
## If we couldn't find the cvsroot path configured in the
## viewvc.conf file, or we don't have a VIEWVC_LINK, then
## don't make the link.
if cvsroot_name and viewvc_link:
flink = '[%s] <a href="%s/%s?root=%s">%s</a>' % (
cvsroot_name, viewvc_link, urllib.quote(file),
cvsroot_name, file)
@@ -358,23 +379,27 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
return ob
def run_query(server, cfg, form_data, viewvc_link):
query = form_to_cvsdb_query(form_data)
query = form_to_cvsdb_query(cfg, form_data)
db = cvsdb.ConnectDatabaseReadOnly(cfg)
db.RunQuery(query)
if not query.commit_list:
return [ ]
commit_list = query.GetCommitList()
if not commit_list:
return [ ], 0
row_limit_reached = query.GetLimitReached()
commits = [ ]
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
current_desc = query.commit_list[0].GetDescription()
for commit in query.commit_list:
current_desc = commit_list[0].GetDescription()
for commit in commit_list:
desc = commit.GetDescription()
if current_desc == desc:
files.append(commit)
@@ -397,7 +422,7 @@ def run_query(server, cfg, form_data, viewvc_link):
return len(commit.files) > 0
commits = filter(_only_with_files, commits)
return commits
return commits, row_limit_reached
def main(server, cfg, viewvc_link):
try:
@@ -406,45 +431,40 @@ def main(server, cfg, viewvc_link):
form_data = FormData(form)
if form_data.valid:
commits = run_query(server, cfg, form_data, viewvc_link)
commits, row_limit_reached = run_query(server, cfg,
form_data, viewvc_link)
query = None
else:
commits = [ ]
row_limit_reached = 0
query = 'skipped'
script_name = server.getenv('SCRIPT_NAME', '')
data = {
docroot = cfg.options.docroot
if docroot is None and viewvc_link:
docroot = viewvc_link + '/' + viewvc.docroot_magic_path
data = TemplateData({
'cfg' : cfg,
'address' : cfg.general.address,
'vsn' : viewvc.__version__,
'repository' : server.escape(form_data.repository, 1),
'branch' : server.escape(form_data.branch, 1),
'directory' : server.escape(form_data.directory, 1),
'file' : server.escape(form_data.file, 1),
'who' : server.escape(form_data.who, 1),
'docroot' : cfg.options.docroot is None \
and viewvc_link + '/' + viewvc.docroot_magic_path \
or cfg.options.docroot,
'repository' : server.escape(form_data.repository),
'branch' : server.escape(form_data.branch),
'directory' : server.escape(form_data.directory),
'file' : server.escape(form_data.file),
'who' : server.escape(form_data.who),
'docroot' : docroot,
'sortby' : form_data.sortby,
'date' : form_data.date,
'query' : query,
'row_limit_reached' : ezt.boolean(row_limit_reached),
'commits' : commits,
'num_commits' : len(commits),
'rss_href' : None,
}
if form_data.hours:
data['hours'] = form_data.hours
else:
data['hours'] = 2
server.header()
'hours' : form_data.hours and form_data.hours or 2,
})
# generate the page
server.header()
template = viewvc.get_view_template(cfg, "query")
template.generate(server.file(), data)
@@ -454,7 +474,3 @@ def main(server, cfg, viewvc_link):
exc_info = debug.GetExceptionData()
server.header(status=exc_info['status'])
debug.PrintException(server, exc_info)
class _item:
def __init__(self, **kw):
vars(self).update(kw)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -16,17 +16,30 @@
# -----------------------------------------------------------------------
import types
import string
import os
import sys
import re
import cgi
# global server object. It will be either a CgiServer or a proxy to
# an AspServer or ModPythonServer object.
# global server object. It will be either a CgiServer, a WsgiServer,
# or a proxy to an AspServer or ModPythonServer object.
server = None
# Simple HTML string escaping. Note that we always escape the
# double-quote character -- ViewVC shouldn't ever need to preserve
# that character as-is, and sometimes needs to embed escaped values
# into HTML attributes.
def escape(s):
s = str(s)
s = s.replace('&', '&amp;')
s = s.replace('>', '&gt;')
s = s.replace('<', '&lt;')
s = s.replace('"', "&quot;")
return s
class Server:
def __init__(self):
self.pageGlobals = {}
@@ -34,6 +47,9 @@ class Server:
def self(self):
return self
def escape(self, s):
return escape(s)
def close(self):
pass
@@ -129,9 +145,6 @@ class CgiServer(Server):
global server
server = self
global cgi
import cgi
def addheader(self, name, value):
self.headers.append((name, value))
@@ -158,11 +171,7 @@ class CgiServer(Server):
if self.iis: url = fix_iis_url(self, url)
self.addheader('Location', url)
self.header(status='301 Moved')
print 'This document is located <a href="%s">here</a>.' % url
sys.exit(0)
def escape(self, s, quote = None):
return cgi.escape(s, quote)
sys.stdout.write('This document is located <a href="%s">here</a>.\n' % url)
def getenv(self, name, value=None):
ret = os.environ.get(name, value)
@@ -176,7 +185,7 @@ class CgiServer(Server):
def FieldStorage(fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
return cgi.FieldStorage(fp, headers, outerboundary, environ,
keep_blank_values, strict_parsing)
keep_blank_values, strict_parsing)
def write(self, s):
sys.stdout.write(s)
@@ -188,6 +197,60 @@ class CgiServer(Server):
return sys.stdout
class WsgiServer(Server):
def __init__(self, environ, start_response):
Server.__init__(self)
self._environ = environ
self._start_response = start_response;
self._headers = []
self._wsgi_write = None
self.headerSent = False
global server
server = self
def addheader(self, name, value):
self._headers.append((name, value))
def header(self, content_type='text/html; charset=UTF-8', status=None):
if not status:
status = "200 OK"
if not self.headerSent:
self.headerSent = True
self._headers.insert(0, ("Content-Type", content_type),)
self._wsgi_write = self._start_response("%s" % status, self._headers)
def redirect(self, url):
"""Redirect client to url. This discards any data that has been queued
to be sent to the user. But there should never by any anyway.
"""
self.addheader('Location', url)
self.header(status='301 Moved')
self._wsgi_write('This document is located <a href="%s">here</a>.' % url)
def getenv(self, name, value=None):
return self._environ.get(name, value)
def params(self):
return cgi.parse(environ=self._environ, fp=self._environ["wsgi.input"])
def FieldStorage(self, fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
return cgi.FieldStorage(self._environ["wsgi.input"], headers,
outerboundary, self._environ, keep_blank_values,
strict_parsing)
def write(self, s):
self._wsgi_write(s)
def flush(self):
pass
def file(self):
return File(self)
class AspServer(ThreadedServer):
def __init__(self, Server, Request, Response, Application):
ThreadedServer.__init__(self)
@@ -219,10 +282,6 @@ class AspServer(ThreadedServer):
def redirect(self, url):
self.response.Redirect(url)
sys.exit()
def escape(self, s, quote = None):
return self.server.HTMLEncode(str(s))
def getenv(self, name, value = None):
ret = self.request.ServerVariables(name)()
@@ -285,9 +344,6 @@ class ModPythonServer(ThreadedServer):
self.request = request
self.headerSent = 0
global cgi
import cgi
def addheader(self, name, value):
self.request.headers_out.add(name, value)
@@ -308,11 +364,7 @@ class ModPythonServer(ThreadedServer):
self.request.headers_out['Location'] = url
self.request.status = mod_python.apache.HTTP_MOVED_TEMPORARILY
self.request.write("You are being redirected to <a href=\"%s\">%s</a>"
% (url, url))
sys.exit()
def escape(self, s, quote = None):
return cgi.escape(s, quote)
% (url, url))
def getenv(self, name, value = None):
try:

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -12,10 +12,6 @@
"""Generic API for implementing authorization checks employed by ViewVC."""
import string
import vclib
class GenericViewVCAuthorizer:
"""Abstract class encapsulating version control authorization routines."""
@@ -29,7 +25,15 @@ class GenericViewVCAuthorizer:
def check_root_access(self, rootname):
"""Return 1 iff the associated username is permitted to read ROOTNAME."""
pass
def check_universal_access(self, rootname):
"""Return 1 if the associated username is permitted to read every
path in the repository at every revision, 0 if the associated
username is prohibited from reading any path in the repository, or
None if no such determination can be made (perhaps because the
cost of making it is too great)."""
pass
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
"""Return 1 iff the associated username is permitted to read
revision REV of the path PATH_PARTS (of type PATHTYPE) in
@@ -44,6 +48,9 @@ class ViewVCAuthorizer(GenericViewVCAuthorizer):
"""The uber-permissive authorizer."""
def check_root_access(self, rootname):
return 1
def check_universal_access(self, rootname):
return 1
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
return 1

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -12,18 +12,24 @@
import vcauth
import vclib
import fnmatch
import string
class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
"""A simple top-level module authorizer."""
def __init__(self, username, params={}):
forbidden = params.get('forbidden', '')
self.forbidden = map(string.strip,
filter(None, string.split(forbidden, ',')))
self.forbidden = map(lambda x: x.strip(),
filter(None, forbidden.split(',')))
def check_root_access(self, rootname):
return 1
def check_universal_access(self, rootname):
# If there aren't any forbidden paths, we can grant universal read
# access. Otherwise, we make no claim.
if not self.forbidden:
return 1
return None
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
# No path? No problem.
if not path_parts:

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2008-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -12,7 +12,6 @@
import vcauth
import vclib
import fnmatch
import string
import re
@@ -29,8 +28,8 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
"""A simple regular-expression-based authorizer."""
def __init__(self, username, params={}):
forbidden = params.get('forbiddenre', '')
self.forbidden = map(lambda x: _split_regexp(string.strip(x)),
filter(None, string.split(forbidden, ',')))
self.forbidden = map(lambda x: _split_regexp(x.strip()),
filter(None, forbidden.split(',')))
def _check_root_path_access(self, root_path):
default = 1
@@ -46,10 +45,17 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
def check_root_access(self, rootname):
return self._check_root_path_access(rootname)
def check_universal_access(self, rootname):
# If there aren't any forbidden regexps, we can grant universal
# read access. Otherwise, we make no claim.
if not self.forbidden:
return 1
return None
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
root_path = rootname
if path_parts:
root_path = root_path + '/' + string.join(path_parts, '/')
root_path = root_path + '/' + '/'.join(path_parts)
if pathtype == vclib.DIR:
root_path = root_path + '/'
else:

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -12,7 +12,6 @@
# (c) 2006 Sergey Lapin <slapin@dataart.com>
import vcauth
import string
import os.path
import debug
@@ -22,7 +21,6 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
"""Subversion authz authorizer module"""
def __init__(self, username, params={}):
self.username = username
self.rootpaths = { } # {root -> { paths -> access boolean for USERNAME }}
# Get the authz file location from a passed-in parameter.
@@ -32,15 +30,33 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
if not os.path.exists(self.authz_file):
raise debug.ViewVCException("Configured authzfile file not found")
# See if the admin wants us to do case normalization of usernames.
self.force_username_case = params.get('force_username_case')
if self.force_username_case == "upper":
self.username = username and username.upper() or username
elif self.force_username_case == "lower":
self.username = username and username.lower() or username
elif not self.force_username_case:
self.username = username
else:
raise debug.ViewVCException("Invalid value for force_username_case "
"option")
def _get_paths_for_root(self, rootname):
if self.rootpaths.has_key(rootname):
return self.rootpaths[rootname]
paths_for_root = { }
# Parse the authz file.
# Parse the authz file, replacing ConfigParser's optionxform()
# method with something that won't futz with the case of the
# option names.
cp = ConfigParser()
cp.read(self.authz_file)
cp.optionxform = lambda x: x
try:
cp.read(self.authz_file)
except:
raise debug.ViewVCException("Unable to parse configured authzfile file")
# Figure out if there are any aliases for the current username
aliases = []
@@ -80,9 +96,9 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
all_groups.append(groupname)
group_member = 0
groupname = groupname.strip()
entries = string.split(cp.get('groups', groupname), ',')
entries = cp.get('groups', groupname).split(',')
for entry in entries:
entry = string.strip(entry)
entry = entry.strip()
if entry == self.username:
group_member = 1
break
@@ -123,13 +139,13 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
# Figure if this path is explicitly allowed or denied to USERNAME.
allow = deny = 0
for user in cp.options(section):
user = string.strip(user)
user = user.strip()
if _userspec_matches_user(user):
# See if the 'r' permission is among the ones granted to
# USER. If so, we can stop looking. (Entry order is not
# relevant -- we'll use the most permissive entry, meaning
# one 'allow' is all we need.)
allow = string.find(cp.get(section, user), 'r') != -1
allow = cp.get(section, user).find('r') != -1
deny = not allow
if allow:
break
@@ -158,7 +174,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
if section.find(':') == -1:
path = section
else:
name, path = string.split(section, ':', 1)
name, path = section.split(':', 1)
if name == rootname:
root_sections.append(section)
continue
@@ -170,14 +186,14 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
# USERNAME, record it.
if allow or deny:
if path != '/':
path = '/' + string.join(filter(None, string.split(path, '/')), '/')
path = '/' + '/'.join(filter(None, path.split('/')))
paths_for_root[path] = allow
# Okay. Superimpose those root-specific values now.
for section in root_sections:
# Get the path again.
name, path = string.split(section, ':', 1)
name, path = section.split(':', 1)
# Check for a specific access determination.
allow, deny = _process_access_section(section)
@@ -186,7 +202,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
# USERNAME, record it.
if allow or deny:
if path != '/':
path = '/' + string.join(filter(None, string.split(path, '/')), '/')
path = '/' + '/'.join(filter(None, path.split('/')))
paths_for_root[path] = allow
# If the root isn't readable, there's no point in caring about all
@@ -207,6 +223,36 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
paths = self._get_paths_for_root(rootname)
return (paths is not None) and 1 or 0
def check_universal_access(self, rootname):
paths = self._get_paths_for_root(rootname)
if not paths: # None or empty.
return 0
# Search the access determinations. If there's a mix, we can't
# claim a universal access determination.
found_allow = 0
found_deny = 0
for access in paths.values():
if access:
found_allow = 1
else:
found_deny = 1
if found_allow and found_deny:
return None
# We didn't find both allowances and denials, so we must have
# found one or the other. Denials only is a universal denial.
if found_deny:
return 0
# ... but allowances only is only a universal allowance if read
# access is granted to the root directory.
if found_allow and paths.has_key('/'):
return 1
# Anything else is indeterminable.
return None
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
# Crawl upward from the path represented by PATH_PARTS toward to
# the root of the repository, looking for an explicitly grant or
@@ -216,7 +262,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
return 0
parts = path_parts[:]
while parts:
path = '/' + string.join(parts, '/')
path = '/' + '/'.join(parts)
if paths.has_key(path):
return paths[path]
del parts[-1]

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -14,7 +14,6 @@
such as CVS.
"""
import string
import types
@@ -76,7 +75,7 @@ class Repository:
"""
pass
def openfile(self, path_parts, rev):
def openfile(self, path_parts, rev, options):
"""Open a file object to read file contents at a given path and revision.
The return value is a 2-tuple of containg the file object and revision
@@ -86,6 +85,8 @@ class Repository:
of the repository. e.g. ["subdir1", "subdir2", "filename"]
rev is the revision of the file to check out
options is a dictionary of implementation specific options
"""
def listdir(self, path_parts, rev, options):
@@ -168,20 +169,29 @@ class Repository:
Return value is a python file object
"""
def annotate(self, path_parts, rev):
"""Return a list of annotate file content lines and a revision.
def annotate(self, path_parts, rev, include_text=False):
"""Return a list of Annotation object, sorted by their
"line_number" components, which describe the lines of given
version of a file.
The result is a list of Annotation objects, sorted by their
line_number components.
"""
The file path is specified as a list of components, relative to
the root of the repository. e.g. ["subdir1", "subdir2", "filename"]
rev is the revision of the item to return information about.
If include_text is true, populate the Annotation objects' "text"
members with the corresponding line of file content; otherwise,
leave that member set to None."""
def revinfo(self, rev):
"""Return information about a global revision
rev is the revision of the item to return information about
Return value is a 4-tuple containing the date, author, log
message, and a list of ChangedPath items representing paths changed
Return value is a 5-tuple containing: the date, author, log
message, a list of ChangedPath items representing paths changed,
and a dictionary mapping property names to property values for
properties stored on an item.
Raise vclib.UnsupportedFeature if the version control system
doesn't support a global revision concept.
@@ -196,7 +206,21 @@ class Repository:
rev is the revision of the item to return information about
"""
def filesize(self, path_parts, rev):
"""Return the size of a versioned file's contents if it can be
obtained without a brute force measurement; -1 otherwise.
NOTE: Callers that require a filesize answer when this function
returns -1 may obtain it by measuring the data returned via
openfile().
The path is specified as a list of components, relative to the root
of the repository. e.g. ["subdir1", "subdir2", "filename"]
rev is the revision of the item to return information about
"""
# ======================================================================
class DirEntry:
@@ -302,7 +326,7 @@ class ItemNotFound(Error):
# use '/' rather than os.sep because this is for user consumption, and
# it was defined using URL separators
if type(path) in (types.TupleType, types.ListType):
path = string.join(path, '/')
path = '/'.join(path)
Error.__init__(self, path)
class InvalidRevision(Error):

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -11,28 +11,52 @@
# -----------------------------------------------------------------------
import os
import os.path
import time
def cvs_strptime(timestr):
return time.strptime(timestr, '%Y/%m/%d %H:%M:%S')[:-1] + (0,)
def canonicalize_rootpath(rootpath):
assert os.path.isabs(rootpath)
return os.path.normpath(rootpath)
def _is_cvsroot(path):
return os.path.exists(os.path.join(path, "CVSROOT", "config"))
def expand_root_parent(parent_path):
# Each subdirectory of PARENT_PATH that contains a child
# "CVSROOT/config" is added the set of returned roots. Or, if the
# PARENT_PATH itself contains a child "CVSROOT/config", then all its
# subdirectories are returned as roots.
assert os.path.isabs(parent_path)
roots = {}
subpaths = os.listdir(parent_path)
cvsroot = os.path.exists(os.path.join(parent_path, "CVSROOT", "config"))
for rootname in subpaths:
rootpath = os.path.join(parent_path, rootname)
if cvsroot \
or (os.path.exists(os.path.join(rootpath, "CVSROOT", "config"))):
if _is_cvsroot(parent_path) or _is_cvsroot(rootpath):
roots[rootname] = canonicalize_rootpath(rootpath)
return roots
def find_root_in_parent(parent_path, rootname):
"""Search PARENT_PATH for a root named ROOTNAME, returning the
canonicalized ROOTPATH of the root if found; return None if no such
root is found."""
# Is PARENT_PATH itself a CVS repository? If so, we allow ROOTNAME
# to be any subdir within it. Otherwise, we expect
# PARENT_PATH/ROOTNAME to be a CVS repository.
assert os.path.isabs(parent_path)
rootpath = os.path.join(parent_path, rootname)
if (_is_cvsroot(parent_path) and os.path.exists(rootpath)) \
or _is_cvsroot(rootpath):
return canonicalize_rootpath(rootpath)
return None
def CVSRepository(name, rootpath, authorizer, utilities, use_rcsparse):
rootpath = canonicalize_rootpath(rootpath)
if use_rcsparse:

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -18,14 +18,17 @@ import os
import os.path
import sys
import stat
import string
import re
import time
import calendar
# ViewVC libs
import compat
import popen
import vclib.ccvs
def _path_join(path_parts):
return '/'.join(path_parts)
class BaseCVSRepository(vclib.Repository):
def __init__(self, name, rootpath, authorizer, utilities):
if not os.path.isdir(rootpath):
@@ -40,6 +43,11 @@ class BaseCVSRepository(vclib.Repository):
if not vclib.check_root_access(self):
raise vclib.ReposNotFound(name)
def open(self):
# See if a universal read access determination can be made.
if self.auth and self.auth.check_universal_access(self.name) == 1:
self.auth = None
def rootname(self):
return self.name
@@ -76,7 +84,7 @@ class BaseCVSRepository(vclib.Repository):
def listdir(self, path_parts, rev, options):
if self.itemtype(path_parts, rev) != vclib.DIR: # does auth-check
raise vclib.Error("Path '%s' is not a directory."
% (string.join(path_parts, "/")))
% (_path_join(path_parts)))
# Only RCS files (*,v) and subdirs are returned.
data = [ ]
@@ -133,17 +141,21 @@ class BaseCVSRepository(vclib.Repository):
if root:
ret = ret_file
else:
ret = string.join(ret_parts, "/")
ret = _path_join(ret_parts)
if v:
ret = ret + ",v"
return ret
def isexecutable(self, path_parts, rev):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
rcsfile = self.rcsfile(path_parts, 1)
return os.access(rcsfile, os.X_OK)
def filesize(self, path_parts, rev):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
return -1
class BinCVSRepository(BaseCVSRepository):
@@ -162,20 +174,28 @@ class BinCVSRepository(BaseCVSRepository):
return revs[-1]
return None
def openfile(self, path_parts, rev):
def openfile(self, path_parts, rev, options):
"""see vclib.Repository.openfile docstring
Option values recognized by this implementation:
cvs_oldkeywords
boolean. true to use the original keyword substitution values.
"""
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
if not rev or rev == 'HEAD' or rev == 'MAIN':
rev_flag = '-p'
else:
rev_flag = '-p' + rev
if options.get('cvs_oldkeywords', 0):
kv_flag = '-ko'
else:
kv_flag = '-kkv'
full_name = self.rcsfile(path_parts, root=1, v=0)
used_rlog = 0
tip_rev = None # used only if we have to fallback to using rlog
fp = self.rcs_popen('co', (rev_flag, full_name), 'rb')
fp = self.rcs_popen('co', (kv_flag, rev_flag, full_name), 'rb')
try:
filename, revision = _parse_co_header(fp)
except COMissingRevision:
@@ -237,7 +257,7 @@ class BinCVSRepository(BaseCVSRepository):
"""
if self.itemtype(path_parts, rev) != vclib.DIR: # does auth-check
raise vclib.Error("Path '%s' is not a directory."
% (string.join(path_parts, "/")))
% (_path_join(path_parts)))
subdirs = options.get('cvs_subdirs', 0)
entries_to_fetch = []
@@ -274,8 +294,7 @@ class BinCVSRepository(BaseCVSRepository):
"""
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
# Invoke rlog
rcsfile = self.rcsfile(path_parts, 1)
@@ -318,13 +337,12 @@ class BinCVSRepository(BaseCVSRepository):
args = rcs_args
return popen.popen(cmd, args, mode, capture_err)
def annotate(self, path_parts, rev=None):
def annotate(self, path_parts, rev=None, include_text=False):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
from vclib.ccvs import blame
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text)
return source, source.revision
def revinfo(self, rev):
@@ -338,11 +356,9 @@ class BinCVSRepository(BaseCVSRepository):
ignore_keyword_subst - boolean, ignore keyword substitution
"""
if self.itemtype(path_parts1, rev1) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts1, "/")))
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts1)))
if self.itemtype(path_parts2, rev2) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts2, "/")))
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts2)))
args = vclib._diff_args(type, options)
if options.get('ignore_keyword_subst', 0):
@@ -550,7 +566,7 @@ def _remove_tag(tag):
def _revision_tuple(revision_string):
"""convert a revision number into a tuple of integers"""
t = tuple(map(int, string.split(revision_string, '.')))
t = tuple(map(int, revision_string.split('.')))
if len(t) % 2 == 0:
return t
raise ValueError
@@ -558,7 +574,7 @@ def _revision_tuple(revision_string):
def _tag_tuple(revision_string):
"""convert a revision number or branch number into a tuple of integers"""
if revision_string:
t = map(int, string.split(revision_string, '.'))
t = map(int, revision_string.split('.'))
l = len(t)
if l == 1:
return ()
@@ -703,7 +719,7 @@ def _parse_log_header(fp):
if state == 1:
if line[0] == '\t':
[ tag, rev ] = map(string.strip, string.split(line, ':'))
[ tag, rev ] = map(lambda x: x.strip(), line.split(':'))
taginfo[tag] = rev
else:
# oops. this line isn't tag info. stop parsing tags.
@@ -711,7 +727,7 @@ def _parse_log_header(fp):
if state == 2:
if line[0] == '\t':
[ locker, rev ] = map(string.strip, string.split(line, ':'))
[ locker, rev ] = map(lambda x: x.strip(), line.split(':'))
lockinfo[rev] = locker
else:
# oops. this line isn't lock info. stop parsing tags.
@@ -820,7 +836,7 @@ def _parse_log_entry(fp):
return None, eof
# parse out a time tuple for the local time
tm = compat.cvs_strptime(match.group(1))
tm = vclib.ccvs.cvs_strptime(match.group(1))
# rlog seems to assume that two-digit years are 1900-based (so, "04"
# comes out as "1904", not "2004").
@@ -831,7 +847,7 @@ def _parse_log_entry(fp):
tm[0] = tm[0] + 100
if tm[0] < EPOCH:
raise ValueError, 'invalid year'
date = compat.timegm(tm)
date = calendar.timegm(tm)
return Revision(rev, date,
# author, state, lines changed
@@ -1022,16 +1038,16 @@ def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs):
file.errors.append("rlog error: %s" % msg)
continue
tag = None
if view_tag == 'MAIN' or view_tag == 'HEAD':
tag = Tag(None, default_branch)
elif taginfo.has_key(view_tag):
tag = Tag(None, taginfo[view_tag])
elif view_tag:
# the tag wasn't found, so skip this file
elif view_tag and (eof != _EOF_FILE):
# the tag wasn't found, so skip this file (unless we already
# know there's nothing left of it to read)
_skip_file(rlog)
eof = 1
else:
tag = None
eof = _EOF_FILE
# we don't care about the specific values -- just the keys and whether
# the values point to branches or revisions. this the fastest way to

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
#
# By using this file, you agree to the terms and conditions set forth in
@@ -26,7 +26,6 @@
#
# -----------------------------------------------------------------------
import string
import re
import time
import math
@@ -100,7 +99,7 @@ class CVSParser(rcsparse.Sink):
# Split deltatext specified by rev to each line.
def deltatext_split(self, rev):
lines = string.split(self.revision_deltatext[rev], '\n')
lines = self.revision_deltatext[rev].split('\n')
if lines[-1] == '':
del lines[-1]
return lines
@@ -139,16 +138,16 @@ class CVSParser(rcsparse.Sink):
adjust = adjust + 1
elif dmatch:
# "d" - Delete command
start_line = string.atoi(dmatch.group(1))
count = string.atoi(dmatch.group(2))
start_line = int(dmatch.group(1))
count = int(dmatch.group(2))
begin = start_line + adjust - 1
del text[begin:begin + count]
adjust = adjust - count
lines_removed_now = lines_removed_now + count
elif amatch:
# "a" - Add command
start_line = string.atoi(amatch.group(1))
count = string.atoi(amatch.group(2))
start_line = int(amatch.group(1))
count = int(amatch.group(2))
add_lines_remaining = count
lines_added_now = lines_added_now + count
else:
@@ -311,13 +310,13 @@ class CVSParser(rcsparse.Sink):
skip = skip - 1
elif dmatch:
# "d" - Delete command
start_line = string.atoi(dmatch.group(1))
count = string.atoi(dmatch.group(2))
start_line = int(dmatch.group(1))
count = int(dmatch.group(2))
line_count = line_count - count
elif amatch:
# "a" - Add command
start_line = string.atoi(amatch.group(1))
count = string.atoi(amatch.group(2))
start_line = int(amatch.group(1))
count = int(amatch.group(2))
skip = count
line_count = line_count + count
else:
@@ -359,8 +358,8 @@ class CVSParser(rcsparse.Sink):
dmatch = self.d_command.match(command)
amatch = self.a_command.match(command)
if dmatch:
start_line = string.atoi(dmatch.group(1))
count = string.atoi(dmatch.group(2))
start_line = int(dmatch.group(1))
count = int(dmatch.group(2))
temp = []
while count > 0:
temp.append(revision)
@@ -368,8 +367,8 @@ class CVSParser(rcsparse.Sink):
self.revision_map = (self.revision_map[:start_line - 1] +
temp + self.revision_map[start_line - 1:])
elif amatch:
start_line = string.atoi(amatch.group(1))
count = string.atoi(amatch.group(2))
start_line = int(amatch.group(1))
count = int(amatch.group(2))
del self.revision_map[start_line:start_line + count]
skip = count
else:
@@ -388,15 +387,15 @@ class CVSParser(rcsparse.Sink):
dmatch = self.d_command.match(command)
amatch = self.a_command.match(command)
if dmatch:
start_line = string.atoi(dmatch.group(1))
count = string.atoi(dmatch.group(2))
start_line = int(dmatch.group(1))
count = int(dmatch.group(2))
adj_begin = start_line + adjust - 1
adj_end = start_line + adjust - 1 + count
del self.revision_map[adj_begin:adj_end]
adjust = adjust - count
elif amatch:
start_line = string.atoi(amatch.group(1))
count = string.atoi(amatch.group(2))
start_line = int(amatch.group(1))
count = int(amatch.group(2))
skip = count
temp = []
while count > 0:
@@ -414,7 +413,7 @@ class CVSParser(rcsparse.Sink):
class BlameSource:
def __init__(self, rcs_file, opt_rev=None):
def __init__(self, rcs_file, opt_rev=None, include_text=False):
# Parse the CVS file
parser = CVSParser()
revision = parser.parse_cvs_file(rcs_file, opt_rev)
@@ -428,6 +427,7 @@ class BlameSource:
self.lines = lines
self.num_lines = count
self.parser = parser
self.include_text = include_text
# keep track of where we are during an iteration
self.idx = -1
@@ -447,6 +447,8 @@ class BlameSource:
line_number = idx + 1
author = self.parser.revision_author[rev]
thisline = self.lines[idx]
if not self.include_text:
thisline = None
### TODO: Put a real date in here.
item = vclib.Annotation(thisline, line_number, rev, prev_rev, author, None)
self.last = item

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -11,7 +11,6 @@
# -----------------------------------------------------------------------
import os
import string
import re
import cStringIO
import tempfile
@@ -22,7 +21,8 @@ import blame
### The functionality shared with bincvs should probably be moved to a
### separate module
from bincvs import BaseCVSRepository, Revision, Tag, _file_log, _log_path, _logsort_date_cmp, _logsort_rev_cmp
from bincvs import BaseCVSRepository, Revision, Tag, _file_log, _log_path, _logsort_date_cmp, _logsort_rev_cmp, _path_join
class CCVSRepository(BaseCVSRepository):
def dirlogs(self, path_parts, rev, entries, options):
@@ -44,7 +44,7 @@ class CCVSRepository(BaseCVSRepository):
"""
if self.itemtype(path_parts, rev) != vclib.DIR: # does auth-check
raise vclib.Error("Path '%s' is not a directory."
% (string.join(path_parts, "/")))
% (part2path(path_parts)))
entries_to_fetch = []
for entry in entries:
if vclib.check_path_access(self, path_parts + [entry.name], None, rev):
@@ -94,8 +94,7 @@ class CCVSRepository(BaseCVSRepository):
dictionary of Tag objects for all tags encountered
"""
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
path = self.rcsfile(path_parts, 1)
sink = TreeSink()
@@ -120,16 +119,14 @@ class CCVSRepository(BaseCVSRepository):
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
if self.itemtype(path_parts1, rev1) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts1, "/")))
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts1)))
if self.itemtype(path_parts2, rev2) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts2, "/")))
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts2)))
temp1 = tempfile.mktemp()
open(temp1, 'wb').write(self.openfile(path_parts1, rev1)[0].getvalue())
open(temp1, 'wb').write(self.openfile(path_parts1, rev1, {})[0].getvalue())
temp2 = tempfile.mktemp()
open(temp2, 'wb').write(self.openfile(path_parts2, rev2)[0].getvalue())
open(temp2, 'wb').write(self.openfile(path_parts2, rev2, {})[0].getvalue())
r1 = self.itemlog(path_parts1, rev1, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
r2 = self.itemlog(path_parts2, rev2, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
@@ -142,25 +139,23 @@ class CCVSRepository(BaseCVSRepository):
return vclib._diff_fp(temp1, temp2, info1, info2,
self.utilities.diff or 'diff', diff_args)
def annotate(self, path_parts, rev=None):
def annotate(self, path_parts, rev=None, include_text=False):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text)
return source, source.revision
def revinfo(self, rev):
raise vclib.UnsupportedFeature
def openfile(self, path_parts, rev=None):
def openfile(self, path_parts, rev, options):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
path = self.rcsfile(path_parts, 1)
sink = COSink(rev)
rcsparse.parse(open(path, 'rb'), sink)
revision = sink.last and sink.last.string
return cStringIO.StringIO(string.join(sink.sstext.text, "\n")), revision
return cStringIO.StringIO('\n'.join(sink.sstext.text)), revision
class MatchingSink(rcsparse.Sink):
"""Superclass for sinks that search for revisions based on tag or number"""
@@ -200,6 +195,7 @@ class InfoSink(MatchingSink):
self.matching_rev = None
self.perfect_match = 0
self.lockinfo = { }
self.saw_revision = False
def define_tag(self, name, revision):
MatchingSink.define_tag(self, name, revision)
@@ -213,10 +209,17 @@ class InfoSink(MatchingSink):
self.entry.absent = 1
raise rcsparse.RCSStopParser
def parse_completed(self):
if not self.saw_revision:
#self.entry.errors.append("No revisions exist on %s" % (view_tag or "MAIN"))
self.entry.absent = 1
def set_locker(self, rev, locker):
self.lockinfo[rev] = locker
def define_revision(self, revision, date, author, state, branches, next):
self.saw_revision = True
if self.perfect_match:
return
@@ -224,14 +227,18 @@ class InfoSink(MatchingSink):
rev = Revision(revision, date, author, state == "dead")
rev.lockinfo = self.lockinfo.get(revision)
# perfect match if revision number matches tag number or if revision is on
# trunk and tag points to trunk. imperfect match if tag refers to a branch
# and this revision is the highest revision so far found on that branch
# perfect match if revision number matches tag number or if
# revision is on trunk and tag points to trunk. imperfect match
# if tag refers to a branch and either a) this revision is the
# highest revision so far found on that branch, or b) this
# revision is the branchpoint.
perfect = ((rev.number == tag.number) or
(not tag.number and len(rev.number) == 2))
if perfect or (tag.is_branch and tag.number == rev.number[:-1] and
(not self.matching_rev or
rev.number > self.matching_rev.number)):
if perfect or (tag.is_branch and \
((tag.number == rev.number[:-1] and
(not self.matching_rev or
rev.number > self.matching_rev.number)) or
(rev.number == tag.number[:-1]))):
self.matching_rev = rev
self.perfect_match = perfect
@@ -287,18 +294,18 @@ class TreeSink(rcsparse.Sink):
deled = 0
if self.head != revision:
changed = 1
lines = string.split(text, '\n')
lines = text.split('\n')
idx = 0
while idx < len(lines):
command = lines[idx]
dmatch = self.d_command.match(command)
idx = idx + 1
if dmatch:
deled = deled + string.atoi(dmatch.group(2))
deled = deled + int(dmatch.group(2))
else:
amatch = self.a_command.match(command)
if amatch:
count = string.atoi(amatch.group(2))
count = int(amatch.group(2))
added = added + count
idx = idx + count
elif command:
@@ -314,12 +321,12 @@ class StreamText:
a_command = re.compile('^a(\d+)\\s(\\d+)')
def __init__(self, text):
self.text = string.split(text, "\n")
self.text = text.split('\n')
def command(self, cmd):
adjust = 0
add_lines_remaining = 0
diffs = string.split(cmd, "\n")
diffs = cmd.split('\n')
if diffs[-1] == "":
del diffs[-1]
if len(diffs) == 0:
@@ -337,22 +344,22 @@ class StreamText:
amatch = self.a_command.match(command)
if dmatch:
# "d" - Delete command
start_line = string.atoi(dmatch.group(1))
count = string.atoi(dmatch.group(2))
start_line = int(dmatch.group(1))
count = int(dmatch.group(2))
begin = start_line + adjust - 1
del self.text[begin:begin + count]
adjust = adjust - count
elif amatch:
# "a" - Add command
start_line = string.atoi(amatch.group(1))
count = string.atoi(amatch.group(2))
start_line = int(amatch.group(1))
count = int(amatch.group(2))
add_lines_remaining = count
else:
raise RuntimeError, 'Error parsing diff commands'
def secondnextdot(s, start):
# find the position the second dot after the start index.
return string.find(s, '.', string.find(s, '.', start) + 1)
return s.find('.', s.find('.', start) + 1)
class COSink(MatchingSink):

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -10,8 +10,17 @@
#
# -----------------------------------------------------------------------
"""This package provides parsing tools for RCS files."""
"""This package provides parsing tools for RCS files.
To use this package, first create a subclass of Sink. This should
declare all the callback methods you care about. Create an instance
of your class, and open() the RCS file you want to read. Then call
parse() to parse the file.
"""
# Make the "Sink" class and the various exception classes visible in this
# scope. That way, applications never need to import any of the
# sub-packages.
from common import *
try:
@@ -23,4 +32,13 @@ except ImportError:
from default import Parser
def parse(file, sink):
"""Parse an RCS file.
Parameters: FILE is the file object to parse. (I.e. an object of the
built-in Python type "file", usually created using Python's built-in
"open()" function). It should be opened in binary mode.
SINK is an instance of (some subclass of) Sink. It's methods will be
called as the file is parsed; see the definition of Sink for the
details.
"""
return Parser().parse(file, sink)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -16,52 +16,199 @@ import calendar
import string
class Sink:
"""Interface to be implemented by clients. The RCS parser calls this as
it parses the RCS file.
All these methods have stub implementations that do nothing, so you only
have to override the callbacks that you care about.
"""
def set_head_revision(self, revision):
"""Reports the head revision for this RCS file.
This is the value of the 'head' header in the admin section of the RCS
file. This function can only be called before admin_completed().
Parameter: REVISION is a string containing a revision number. This is
an actual revision number, not a branch number.
"""
pass
def set_principal_branch(self, branch_name):
"""Reports the principal branch for this RCS file. This is only called
if the principal branch is not trunk.
This is the value of the 'branch' header in the admin section of the RCS
file. This function can only be called before admin_completed().
Parameter: BRANCH_NAME is a string containing a branch number. If this
function is called, the parameter is typically "1.1.1", indicating the
vendor branch.
"""
pass
def set_access(self, accessors):
"""Reports the access control list for this RCS file. This function is
only called if the ACL is set. If this function is not called then
there is no ACL and all users are allowed access.
This is the value of the 'access' header in the admin section of the RCS
file. This function can only be called before admin_completed().
Parameter: ACCESSORS is a list of strings. Each string is a username.
The user is allowed access if and only if their username is in the list,
OR the user owns the RCS file on disk, OR the user is root.
Note that CVS typically doesn't use this field.
"""
pass
def define_tag(self, name, revision):
"""Reports a tag or branch definition. This function will be called
once for each tag or branch.
This is taken from the 'symbols' header in the admin section of the RCS
file. This function can only be called before admin_completed().
Parameters: NAME is a string containing the tag or branch name.
REVISION is a string containing a revision number. This may be
an actual revision number (for a tag) or a branch number.
The revision number consists of a number of decimal components separated
by dots. There are three common forms. If there are an odd number of
components, it's a branch. Otherwise, if the next-to-last component is
zero, it's a branch (and the next-to-last component is an artifact of
CVS and should not be shown to the user). Otherwise, it's a tag.
This function is called in the order that the tags appear in the RCS
file header. For CVS, this appears to be in reverse chronological
order of tag/branch creation.
"""
pass
def set_locker(self, revision, locker):
"""Reports a lock on this RCS file. This function will be called once
for each lock.
This is taken from the 'locks' header in the admin section of the RCS
file. This function can only be called before admin_completed().
Parameters: REVISION is a string containing a revision number. This is
an actual revision number, not a branch number.
LOCKER is a string containing a username.
"""
pass
def set_locking(self, mode):
"""Used to signal locking mode.
"""Signals strict locking mode. This function will be called if and
only if the RCS file is in strict locking mode.
Called with mode argument 'strict' if strict locking
Not called when no locking used."""
This is taken from the 'strict' header in the admin section of the RCS
file. This function can only be called before admin_completed().
Parameters: MODE is always the string 'strict'.
"""
pass
def set_comment(self, comment):
"""Reports the comment for this RCS file.
This is the value of the 'comment' header in the admin section of the
RCS file. This function can only be called before admin_completed().
Parameter: COMMENT is a string containing the comment. This may be
multi-line.
This field does not seem to be used by CVS.
"""
pass
def set_expansion(self, mode):
"""Reports the keyword expansion mode for this RCS file.
This is the value of the 'expand' header in the admin section of the
RCS file. This function can only be called before admin_completed().
Parameter: MODE is a string containing the keyword expansion mode.
Possible values include 'o' and 'b', amongst others.
"""
pass
def admin_completed(self):
"""Reports that the initial RCS header has been parsed. This function is
called exactly once.
"""
pass
def define_revision(self, revision, timestamp, author, state,
branches, next):
"""Reports metadata about a single revision.
This function is called for each revision. It is called later than
admin_completed() and earlier than tree_completed().
Parameter: REVISION is a revision number, as a string. This is an
actual revision number, not a branch number.
TIMESTAMP is the date and time that the revision was created, as an
integer number of seconds since the epoch. (I.e. "UNIX time" format).
AUTHOR is the author name, as a string.
STATE is the state of the revision, as a string. Common values are
"Exp" and "dead".
BRANCHES is a list of strings, with each string being an actual
revision number (not a branch number). For each branch which is based
on this revision and has commits, the revision number of the first
branch commit is listed here.
NEXT is either None or a string representing an actual revision number
(not a branch number).
When on trunk, NEXT points to what humans might consider to be the
'previous' revision number. For example, 1.3's NEXT is 1.2.
However, on a branch, NEXT really does point to what humans would
consider to be the 'next' revision number. For example, 1.1.2.1's
NEXT would be 1.1.2.2.
In other words, NEXT always means "where to find the next deltatext
that you need this revision to retrieve".
"""
pass
def tree_completed(self):
"""Reports that the RCS revision tree has been parsed. This function is
called exactly once. This function will be called later than
admin_completed().
"""
pass
def set_description(self, description):
"""Reports the description from the RCS file. This is set using the
"-m" flag to "cvs add". However, many CVS users don't use that option,
so this is often empty.
This function is called once, after tree_completed().
Parameter: DESCRIPTION is a string containing the description. This may
be multi-line.
"""
pass
def set_revision_info(self, revision, log, text):
"""Reports the log message and contents of a CVS revision.
This function is called for each revision. It is called later than
set_description().
Parameters: REVISION is a string containing the actual revision number.
LOG is a string containing the log message. This may be multi-line.
TEXT is the contents of the file in this revision, either as full-text or
as a diff. This is usually multi-line, and often quite large and/or
binary.
"""
pass
def parse_completed(self):
"""Reports that parsing an RCS file is complete.
This function is called once. After it is called, no more calls will be
made via this interface.
"""
pass
@@ -194,7 +341,8 @@ class _Parser:
else:
# Chew up "newphrase"
# warn("Unexpected RCS token: $token\n")
pass
while self.ts.get() != ';':
pass
else:
if f is None:
self.ts.unget(token)
@@ -208,7 +356,7 @@ class _Parser:
date = self.ts.get()
self.ts.match(';')
# Convert date into timestamp
# Convert date into standard UNIX time format (seconds since epoch)
date_fields = string.split(date, '.')
# According to rcsfile(5): the year "contains just the last two
# digits of the year for years from 1900 through 1999, and all the
@@ -218,8 +366,11 @@ class _Parser:
date_fields = map(string.atoi, date_fields)
EPOCH = 1970
if date_fields[0] < EPOCH:
raise ValueError, 'invalid year'
timestamp = calendar.timegm(tuple(date_fields) + (0, 0, 0,))
raise ValueError, 'invalid year for revision %s' % (revision,)
try:
timestamp = calendar.timegm(tuple(date_fields) + (0, 0, 0,))
except ValueError, e:
raise ValueError, 'invalid date for revision %s: %s' % (revision, e,)
# Parse author
### NOTE: authors containing whitespace are violations of the
@@ -255,6 +406,7 @@ class _Parser:
# group 15;
# permissions 644;
# hardlinks @configure.in@;
# commitid mLiHw3bulRjnTDGr;
# this is "newphrase" in RCSFILE(5). we just want to skip over these.
while 1:
token = self.ts.get()
@@ -298,6 +450,15 @@ class _Parser:
self.sink.set_revision_info(revision, log, text)
def parse(self, file, sink):
"""Parse an RCS file.
Parameters: FILE is the file object to parse. (I.e. an object of the
built-in Python type "file", usually created using Python's built-in
"open()" function).
SINK is an instance of (some subclass of) Sink. It's methods will be
called as the file is parsed; see the definition of Sink for the
details.
"""
self.ts = self.stream_class(file)
self.sink = sink

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -19,7 +19,11 @@ import string
import common
class _TokenStream:
token_term = string.whitespace + ';:'
token_term = string.whitespace + ";:"
try:
token_term = frozenset(token_term)
except NameError:
pass
# the algorithm is about the same speed for any CHUNK_SIZE chosen.
# grab a good-sized chunk, but not too large to overwhelm memory.
@@ -44,15 +48,17 @@ class _TokenStream:
# out more complex solutions.
buf = self.buf
lbuf = len(buf)
idx = self.idx
while 1:
if idx == len(buf):
if idx == lbuf:
buf = self.rcsfile.read(self.CHUNK_SIZE)
if buf == '':
# signal EOF by returning None as the token
del self.buf # so we fail if get() is called again
return None
lbuf = len(buf)
idx = 0
if buf[idx] not in string.whitespace:
@@ -60,7 +66,7 @@ class _TokenStream:
idx = idx + 1
if buf[idx] == ';' or buf[idx] == ':':
if buf[idx] in ';:':
self.buf = buf
self.idx = idx + 1
return buf[idx]
@@ -70,17 +76,18 @@ class _TokenStream:
token = ''
while 1:
# find token characters in the current buffer
while end < len(buf) and buf[end] not in self.token_term:
while end < lbuf and buf[end] not in self.token_term:
end = end + 1
token = token + buf[idx:end]
if end < len(buf):
if end < lbuf:
# we stopped before the end, so we have a full token
idx = end
break
# we stopped at the end of the buffer, so we may have a partial token
buf = self.rcsfile.read(self.CHUNK_SIZE)
lbuf = len(buf)
idx = end = 0
self.buf = buf
@@ -94,22 +101,24 @@ class _TokenStream:
chunks = [ ]
while 1:
if idx == len(buf):
if idx == lbuf:
idx = 0
buf = self.rcsfile.read(self.CHUNK_SIZE)
if buf == '':
raise RuntimeError, 'EOF'
lbuf = len(buf)
i = string.find(buf, '@', idx)
if i == -1:
chunks.append(buf[idx:])
idx = len(buf)
idx = lbuf
continue
if i == len(buf) - 1:
if i == lbuf - 1:
chunks.append(buf[idx:i])
idx = 0
buf = '@' + self.rcsfile.read(self.CHUNK_SIZE)
if buf == '@':
raise RuntimeError, 'EOF'
lbuf = len(buf)
continue
if buf[i + 1] == '@':
chunks.append(buf[idx:i+1])

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -15,17 +15,50 @@
import os
import os.path
import re
import urllib
_re_url = re.compile('^(http|https|file|svn|svn\+[^:]+)://')
def canonicalize_rootpath(rootpath):
def _canonicalize_path(path):
import svn.core
try:
import svn.core
return svn.core.svn_path_canonicalize(rootpath)
except:
if re.search(_re_url, rootpath):
return rootpath[-1] == '/' and rootpath[:-1] or rootpath
return os.path.normpath(rootpath)
return svn.core.svn_path_canonicalize(path)
except AttributeError: # svn_path_canonicalize() appeared in 1.4.0 bindings
# There's so much more that we *could* do here, but if we're
# here at all its because there's a really old Subversion in
# place, and those older Subversion versions cared quite a bit
# less about the specifics of path canonicalization.
if re.search(_re_url, path):
return path.rstrip('/')
else:
return os.path.normpath(path)
def canonicalize_rootpath(rootpath):
# Try to canonicalize the rootpath using Subversion semantics.
rootpath = _canonicalize_path(rootpath)
# ViewVC's support for local repositories is more complete and more
# performant than its support for remote ones, so if we're on a
# Unix-y system and we have a file:/// URL, convert it to a local
# path instead.
if os.name == 'posix':
rootpath_lower = rootpath.lower()
if rootpath_lower in ['file://localhost',
'file://localhost/',
'file://',
'file:///'
]:
return '/'
if rootpath_lower.startswith('file://localhost/'):
rootpath = os.path.normpath(urllib.unquote(rootpath[16:]))
elif rootpath_lower.startswith('file:///'):
rootpath = os.path.normpath(urllib.unquote(rootpath[7:]))
# Ensure that we have an absolute path (or URL), and return.
if not re.search(_re_url, rootpath):
assert os.path.isabs(rootpath)
return rootpath
def expand_root_parent(parent_path):
@@ -35,6 +68,7 @@ def expand_root_parent(parent_path):
else:
# Any subdirectories of PARENT_PATH which themselves have a child
# "format" are returned as roots.
assert os.path.isabs(parent_path)
subpaths = os.listdir(parent_path)
for rootname in subpaths:
rootpath = os.path.join(parent_path, rootname)
@@ -43,6 +77,20 @@ def expand_root_parent(parent_path):
return roots
def find_root_in_parent(parent_path, rootname):
"""Search PARENT_PATH for a root named ROOTNAME, returning the
canonicalized ROOTPATH of the root if found; return None if no such
root is found."""
if not re.search(_re_url, parent_path):
assert os.path.isabs(parent_path)
rootpath = os.path.join(parent_path, rootname)
format_path = os.path.join(rootpath, "format")
if os.path.exists(format_path):
return canonicalize_rootpath(rootpath)
return None
def SubversionRepository(name, rootpath, authorizer, utilities, config_dir):
rootpath = canonicalize_rootpath(rootpath)
if re.search(_re_url, rootpath):

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -15,13 +15,14 @@
import vclib
import sys
import os
import string
import re
import tempfile
import popen2
import time
import urllib
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, \
_compare_paths, _path_parts, _cleanup_path, \
_rev2optrev, _fix_subversion_exception, \
_split_revprops, _canonicalize_path
from svn import core, delta, client, wc, ra
@@ -52,13 +53,71 @@ def get_directory_props(ra_session, path, rev):
props = ra.svn_ra_get_dir(ra_session, path, rev)
return props
def client_log(url, start_rev, end_rev, log_limit, include_changes,
cross_copies, cb_func, ctx):
include_changes = include_changes and 1 or 0
cross_copies = cross_copies and 1 or 0
try:
client.svn_client_log4([url], start_rev, start_rev, end_rev,
log_limit, include_changes, not cross_copies,
0, None, cb_func, ctx)
except AttributeError:
# Wrap old svn_log_message_receiver_t interface with a
# svn_log_entry_t one.
def cb_convert(paths, revision, author, date, message, pool):
class svn_log_entry_t:
pass
log_entry = svn_log_entry_t()
log_entry.changed_paths = paths
log_entry.revision = revision
log_entry.revprops = { core.SVN_PROP_REVISION_LOG : message,
core.SVN_PROP_REVISION_AUTHOR : author,
core.SVN_PROP_REVISION_DATE : date,
}
cb_func(log_entry, pool)
client.svn_client_log2([url], start_rev, end_rev, log_limit,
include_changes, not cross_copies, cb_convert, ctx)
def setup_client_ctx(config_dir):
# Ensure that the configuration directory exists.
core.svn_config_ensure(config_dir)
# Fetch the configuration (and 'config' bit thereof).
cfg = core.svn_config_get_config(config_dir)
config = cfg.get(core.SVN_CONFIG_CATEGORY_CONFIG)
# Here's the compat-sensitive part: try to use
# svn_cmdline_create_auth_baton(), and fall back to making our own
# if that fails.
try:
auth_baton = core.svn_cmdline_create_auth_baton(1, None, None, config_dir,
1, 1, config, None)
except AttributeError:
auth_baton = core.svn_auth_open([
client.svn_client_get_simple_provider(),
client.svn_client_get_username_provider(),
client.svn_client_get_ssl_server_trust_file_provider(),
client.svn_client_get_ssl_client_cert_file_provider(),
client.svn_client_get_ssl_client_cert_pw_file_provider(),
])
if config_dir is not None:
core.svn_auth_set_parameter(auth_baton,
core.SVN_AUTH_PARAM_CONFIG_DIR,
config_dir)
# Create, setup, and return the client context baton.
ctx = client.svn_client_create_context()
ctx.config = cfg
ctx.auth_baton = auth_baton
return ctx
### END COMPATABILITY CODE ###
class LogCollector:
### TODO: Make this thing authz-aware
def __init__(self, path, show_all_logs, lockinfo):
def __init__(self, path, show_all_logs, lockinfo, access_check_func):
# This class uses leading slashes for paths internally
if not path:
self.path = '/'
@@ -67,8 +126,16 @@ class LogCollector:
self.logs = []
self.show_all_logs = show_all_logs
self.lockinfo = lockinfo
self.access_check_func = access_check_func
self.done = False
def add_log(self, log_entry, pool):
if self.done:
return
paths = log_entry.changed_paths
revision = log_entry.revision
msg, author, date, revprops = _split_revprops(log_entry.revprops)
def add_log(self, paths, revision, author, date, message, pool):
# Changed paths have leading slashes
changed_paths = paths.keys()
changed_paths.sort(lambda a, b: _compare_paths(a, b))
@@ -82,19 +149,23 @@ class LogCollector:
if changed_path != self.path:
# If a parent of our path was copied, our "next previous"
# (huh?) path will exist elsewhere (under the copy source).
if (string.rfind(self.path, changed_path) == 0) and \
if (self.path.rfind(changed_path) == 0) and \
self.path[len(changed_path)] == '/':
change = paths[changed_path]
if change.copyfrom_path:
this_path = change.copyfrom_path + self.path[len(changed_path):]
if self.show_all_logs or this_path:
entry = Revision(revision, _datestr_to_date(date), author, message, None,
self.lockinfo, self.path[1:], None, None)
self.logs.append(entry)
if self.access_check_func is None \
or self.access_check_func(self.path[1:], revision):
entry = Revision(revision, date, author, msg, None, self.lockinfo,
self.path[1:], None, None)
self.logs.append(entry)
else:
self.done = True
if this_path:
self.path = this_path
def temp_checkout(svnrepos, path, rev):
def cat_to_tempfile(svnrepos, path, rev):
"""Check out file revision to temporary file"""
temp = tempfile.mktemp()
stream = core.svn_stream_from_aprfile(temp)
@@ -155,21 +226,8 @@ class RemoteSubversionRepository(vclib.Repository):
def open(self):
# Setup the client context baton, complete with non-prompting authstuffs.
# TODO: svn_cmdline_setup_auth_baton() is mo' better (when available)
core.svn_config_ensure(self.config_dir)
self.ctx = client.svn_client_ctx_t()
self.ctx.auth_baton = core.svn_auth_open([
client.svn_client_get_simple_provider(),
client.svn_client_get_username_provider(),
client.svn_client_get_ssl_server_trust_file_provider(),
client.svn_client_get_ssl_client_cert_file_provider(),
client.svn_client_get_ssl_client_cert_pw_file_provider(),
])
self.ctx.config = core.svn_config_get_config(self.config_dir)
if self.config_dir is not None:
core.svn_auth_set_parameter(self.ctx.auth_baton,
core.SVN_AUTH_PARAM_CONFIG_DIR,
self.config_dir)
self.ctx = setup_client_ctx(self.config_dir)
ra_callbacks = ra.svn_ra_callbacks_t()
ra_callbacks.auth_baton = self.ctx.auth_baton
self.ra_session = ra.svn_ra_open(self.rootpath, ra_callbacks, None,
@@ -177,6 +235,10 @@ class RemoteSubversionRepository(vclib.Repository):
self.youngest = ra.svn_ra_get_latest_revnum(self.ra_session)
self._dirent_cache = { }
self._revinfo_cache = { }
# See if a universal read access determination can be made.
if self.auth and self.auth.check_universal_access(self.name) == 1:
self.auth = None
def rootname(self):
return self.name
@@ -211,25 +273,23 @@ class RemoteSubversionRepository(vclib.Repository):
raise vclib.ItemNotFound(path_parts)
return pathtype
def openfile(self, path_parts, rev):
def openfile(self, path_parts, rev, options):
path = self._getpath(path_parts)
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." % path)
rev = self._getrev(rev)
url = self._geturl(path)
tmp_file = tempfile.mktemp()
stream = core.svn_stream_from_aprfile(tmp_file)
### rev here should be the last history revision of the URL
client.svn_client_cat(core.Stream(stream), url, _rev2optrev(rev), self.ctx)
core.svn_stream_close(stream)
return SelfCleanFP(tmp_file), self._get_last_history_rev(path_parts, rev)
fp = SelfCleanFP(cat_to_tempfile(self, path, rev))
lh_rev, c_rev = self._get_last_history_rev(path_parts, rev)
return fp, lh_rev
def listdir(self, path_parts, rev, options):
path = self._getpath(path_parts)
if self.itemtype(path_parts, rev) != vclib.DIR: # does auth-check
raise vclib.Error("Path '%s' is not a directory." % path)
rev = self._getrev(rev)
entries = [ ]
entries = []
dirents, locks = self._get_dirents(path, rev)
for name in dirents.keys():
entry = dirents[name]
@@ -237,8 +297,9 @@ class RemoteSubversionRepository(vclib.Repository):
kind = vclib.DIR
elif entry.kind == core.svn_node_file:
kind = vclib.FILE
if vclib.check_path_access(self, path_parts + [name], kind, rev):
entries.append(vclib.DirEntry(name, kind))
else:
kind = None
entries.append(vclib.DirEntry(name, kind))
return entries
def dirlogs(self, path_parts, rev, entries, options):
@@ -249,12 +310,14 @@ class RemoteSubversionRepository(vclib.Repository):
dirents, locks = self._get_dirents(path, rev)
for entry in entries:
entry_path_parts = path_parts + [entry.name]
if not vclib.check_path_access(self, entry_path_parts, entry.kind, rev):
dirent = dirents.get(entry.name, None)
# dirents is authz-sanitized, so ensure the entry is found therein.
if dirent is None:
continue
dirent = dirents[entry.name]
entry.date, entry.author, entry.log, changes = \
self.revinfo(dirent.created_rev)
entry.rev = dirent.created_rev
# Get authz-sanitized revision metadata.
entry.date, entry.author, entry.log, revprops, changes = \
self._revinfo(dirent.created_rev)
entry.rev = str(dirent.created_rev)
entry.size = dirent.size
entry.lockinfo = None
if locks.has_key(entry.name):
@@ -267,29 +330,51 @@ class RemoteSubversionRepository(vclib.Repository):
rev = self._getrev(rev)
url = self._geturl(path)
# Use ls3 to fetch the lock status for this item.
lockinfo = None
basename = path_parts and path_parts[-1] or ""
dirents, locks = list_directory(url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
if locks.has_key(basename):
lockinfo = locks[basename].owner
# If this is a file, fetch the lock status and size (as of REV)
# for this item.
lockinfo = size_in_rev = None
if path_type == vclib.FILE:
basename = path_parts[-1]
list_url = self._geturl(self._getpath(path_parts[:-1]))
dirents, locks = list_directory(list_url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
if locks.has_key(basename):
lockinfo = locks[basename].owner
if dirents.has_key(basename):
size_in_rev = dirents[basename].size
# Special handling for the 'svn_latest_log' scenario.
### FIXME: Don't like this hack. We should just introduce
### something more direct in the vclib API.
if options.get('svn_latest_log', 0):
dir_lh_rev, dir_c_rev = self._get_last_history_rev(path_parts, rev)
date, author, log, revprops, changes = self._revinfo(dir_lh_rev)
return [vclib.Revision(dir_lh_rev, str(dir_lh_rev), date, author,
None, log, size_in_rev, lockinfo)]
def _access_checker(check_path, check_rev):
return vclib.check_path_access(self, _path_parts(check_path),
path_type, check_rev)
# It's okay if we're told to not show all logs on a file -- all
# the revisions should match correctly anyway.
lc = LogCollector(path, options.get('svn_show_all_dir_logs', 0), lockinfo)
lc = LogCollector(path, options.get('svn_show_all_dir_logs', 0),
lockinfo, _access_checker)
cross_copies = options.get('svn_cross_copies', 0)
log_limit = 0
if limit:
log_limit = first + limit
client.svn_client_log2([url], _rev2optrev(rev), _rev2optrev(1),
log_limit, 1, not cross_copies,
lc.add_log, self.ctx)
client_log(url, _rev2optrev(rev), _rev2optrev(1), log_limit, 1,
cross_copies, lc.add_log, self.ctx)
revs = lc.logs
revs.sort()
prev = None
for rev in revs:
# Swap out revision info with stuff from the cache (which is
# authz-sanitized).
rev.date, rev.author, rev.log, revprops, changes \
= self._revinfo(rev.number)
rev.prev = prev
prev = rev
revs.reverse()
@@ -309,13 +394,25 @@ class RemoteSubversionRepository(vclib.Repository):
_rev2optrev(rev), 0, self.ctx)
return pairs and pairs[0][1] or {}
def annotate(self, path_parts, rev):
def annotate(self, path_parts, rev, include_text=False):
path = self._getpath(path_parts)
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." % path)
rev = self._getrev(rev)
url = self._geturl(path)
# Examine logs for the file to determine the oldest revision we are
# permitted to see.
log_options = {
'svn_cross_copies' : 1,
'svn_show_all_dir_logs' : 1,
}
revs = self.itemlog(path_parts, rev, vclib.SORTBY_REV, 0, 0, log_options)
oldest_rev = revs[-1].number
# Now calculate the annotation data. Note that we'll not
# inherently trust the provided author and date, because authz
# rules might necessitate that we strip that information out.
blame_data = []
def _blame_cb(line_no, revision, author, date,
@@ -323,21 +420,27 @@ class RemoteSubversionRepository(vclib.Repository):
prev_rev = None
if revision > 1:
prev_rev = revision - 1
blame_data.append(vclib.Annotation(line, line_no+1, revision, prev_rev,
author, None))
client.svn_client_blame(url, _rev2optrev(1), _rev2optrev(rev),
_blame_cb, self.ctx)
# If we have an invalid revision, clear the date and author
# values. Otherwise, if we have authz filtering to do, use the
# revinfo cache to do so.
if revision < 0:
date = author = None
elif self.auth:
date, author, msg, revprops, changes = self._revinfo(revision)
# Strip text if the caller doesn't want it.
if not include_text:
line = None
blame_data.append(vclib.Annotation(line, line_no + 1, revision, prev_rev,
author, date))
client.blame2(url, _rev2optrev(rev), _rev2optrev(oldest_rev),
_rev2optrev(rev), _blame_cb, self.ctx)
return blame_data, rev
def revinfo(self, rev):
rev = self._getrev(rev)
cached_info = self._revinfo_cache.get(rev)
if not cached_info:
cached_info = self._revinfo_raw(rev)
self._revinfo_cache[rev] = cached_info
return cached_info[0], cached_info[1], cached_info[2], cached_info[3]
return self._revinfo(rev, 1)
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
p1 = self._getpath(path_parts1)
@@ -352,16 +455,17 @@ class RemoteSubversionRepository(vclib.Repository):
args = vclib._diff_args(type, options)
def _date_from_rev(rev):
date, author, msg, changes = self.revinfo(rev)
date, author, msg, revprops, changes = self._revinfo(rev)
return date
try:
temp1 = temp_checkout(self, p1, r1)
temp2 = temp_checkout(self, p2, r2)
temp1 = cat_to_tempfile(self, p1, r1)
temp2 = cat_to_tempfile(self, p2, r2)
info1 = p1, _date_from_rev(r1), r1
info2 = p2, _date_from_rev(r2), r2
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == vclib.svn.core.SVN_ERR_FS_NOT_FOUND:
raise vclib.InvalidRevision
raise
@@ -370,15 +474,27 @@ class RemoteSubversionRepository(vclib.Repository):
props = self.itemprops(path_parts, rev) # does authz-check
return props.has_key(core.SVN_PROP_EXECUTABLE)
def filesize(self, path_parts, rev):
path = self._getpath(path_parts)
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." % path)
rev = self._getrev(rev)
dirents, locks = self._get_dirents(self._getpath(path_parts[:-1]), rev)
dirent = dirents.get(path_parts[-1], None)
return dirent.size
def _getpath(self, path_parts):
return string.join(path_parts, '/')
return '/'.join(path_parts)
def _getrev(self, rev):
if rev is None or rev == 'HEAD':
return self.youngest
try:
if type(rev) == type(''):
while rev[0] == 'r':
rev = rev[1:]
rev = int(rev)
except ValueError:
except:
raise vclib.InvalidRevision(rev)
if (rev < 0) or (rev > self.youngest):
raise vclib.InvalidRevision(rev)
@@ -387,58 +503,139 @@ class RemoteSubversionRepository(vclib.Repository):
def _geturl(self, path=None):
if not path:
return self.rootpath
return self.rootpath + '/' + urllib.quote(path, "/*~")
path = self.rootpath + '/' + urllib.quote(path)
return _canonicalize_path(path)
def _get_dirents(self, path, rev):
"""Return a 2-type of dirents and locks, possibly reading/writing
from a local cache of that information."""
from a local cache of that information. This functions performs
authz checks, stripping out unreadable dirents."""
dir_url = self._geturl(path)
path_parts = _path_parts(path)
if path:
key = str(rev) + '/' + path
else:
key = str(rev)
# Ensure that the cache gets filled...
dirents_locks = self._dirent_cache.get(key)
if not dirents_locks:
dirents, locks = list_directory(dir_url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
tmp_dirents, locks = list_directory(dir_url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
dirents = {}
for name, dirent in tmp_dirents.items():
dirent_parts = path_parts + [name]
kind = dirent.kind
if (kind == core.svn_node_dir or kind == core.svn_node_file) \
and vclib.check_path_access(self, dirent_parts,
kind == core.svn_node_dir \
and vclib.DIR or vclib.FILE, rev):
lh_rev, c_rev = self._get_last_history_rev(dirent_parts, rev)
dirent.created_rev = lh_rev
dirents[name] = dirent
dirents_locks = [dirents, locks]
self._dirent_cache[key] = dirents_locks
# ...then return the goodies from the cache.
return dirents_locks[0], dirents_locks[1]
def _get_last_history_rev(self, path_parts, rev):
"""Return the a 2-tuple which contains:
- the last interesting revision equal to or older than REV in
the history of PATH_PARTS.
- the created_rev of of PATH_PARTS as of REV."""
path = self._getpath(path_parts)
url = self._geturl(self._getpath(path_parts))
optrev = _rev2optrev(rev)
# Get the last-changed-rev.
revisions = []
def _info_cb(path, info, pool, retval=revisions):
revisions.append(info.last_changed_rev)
client.svn_client_info(url, optrev, optrev, _info_cb, 0, self.ctx)
return revisions[0]
def _revinfo_raw(self, rev):
# return 5-tuple (date, author, message, changes)
optrev = _rev2optrev(rev)
revs = []
last_changed_rev = revisions[0]
def _log_cb(changed_paths, revision, author,
datestr, message, pool, retval=revs):
date = _datestr_to_date(datestr)
# Now, this object might not have been directly edited since the
# last-changed-rev, but it might have been the child of a copy.
# To determine this, we'll run a potentially no-op log between
# LAST_CHANGED_REV and REV.
lc = LogCollector(path, 1, None, None)
client_log(url, optrev, _rev2optrev(last_changed_rev), 1, 1, 0,
lc.add_log, self.ctx)
revs = lc.logs
if revs:
revs.sort()
return revs[0].number, last_changed_rev
else:
return last_changed_rev, last_changed_rev
def _revinfo_fetch(self, rev, include_changed_paths=0):
need_changes = include_changed_paths or self.auth
revs = []
def _log_cb(log_entry, pool, retval=revs):
# If Subversion happens to call us more than once, we choose not
# to care.
if retval:
return
revision = log_entry.revision
msg, author, date, revprops = _split_revprops(log_entry.revprops)
action_map = { 'D' : vclib.DELETED,
'A' : vclib.ADDED,
'R' : vclib.REPLACED,
'M' : vclib.MODIFIED,
}
paths = (changed_paths or {}).keys()
# Easy out: if we won't use the changed-path info, just return a
# changes-less tuple.
if not need_changes:
return revs.append([date, author, msg, revprops, None])
# Subversion 1.5 and earlier didn't offer the 'changed_paths2'
# hash, and in Subversion 1.6, it's offered but broken.
try:
changed_paths = log_entry.changed_paths2
paths = (changed_paths or {}).keys()
except:
changed_paths = log_entry.changed_paths
paths = (changed_paths or {}).keys()
paths.sort(lambda a, b: _compare_paths(a, b))
# If we get this far, our caller needs changed-paths, or we need
# them for authz-related sanitization.
changes = []
found_readable = found_unreadable = 0
for path in paths:
pathtype = None
change = changed_paths[path]
# svn_log_changed_path_t (which we might get instead of the
# svn_log_changed_path2_t we'd prefer) doesn't have the
# 'node_kind' member.
pathtype = None
if hasattr(change, 'node_kind'):
if change.node_kind == core.svn_node_dir:
pathtype = vclib.DIR
elif change.node_kind == core.svn_node_file:
pathtype = vclib.FILE
# svn_log_changed_path2_t only has the 'text_modified' and
# 'props_modified' bits in Subversion 1.7 and beyond. And
# svn_log_changed_path_t is without.
text_modified = props_modified = 0
if hasattr(change, 'text_modified'):
if change.text_modified == core.svn_tristate_true:
text_modified = 1
if hasattr(change, 'props_modified'):
if change.props_modified == core.svn_tristate_true:
props_modified = 1
# Wrong, diddily wrong wrong wrong. Can you say,
# "Manufacturing data left and right because it hurts to
# figure out the right stuff?"
action = action_map.get(change.action, vclib.MODIFIED)
### Wrong, diddily wrong wrong wrong. Can you say,
### "Manufacturing data left and right because it hurts to
### figure out the right stuff?"
if change.copyfrom_path and change.copyfrom_rev:
is_copy = 1
base_path = change.copyfrom_path
@@ -451,31 +648,61 @@ class RemoteSubversionRepository(vclib.Repository):
base_path = path
base_rev = revision - 1
### Check authz rules (we lie about the path type)
# Check authz rules (sadly, we have to lie about the path type)
parts = _path_parts(path)
if vclib.check_path_access(self, parts, vclib.FILE, revision):
if is_copy and base_path and (base_path != path):
parts = _path_parts(base_path)
if vclib.check_path_access(self, parts, vclib.FILE, base_rev):
if not vclib.check_path_access(self, parts, vclib.FILE, base_rev):
is_copy = 0
base_path = None
base_rev = None
found_unreadable = 1
changes.append(SVNChangedPath(path, revision, pathtype, base_path,
base_rev, action, is_copy, 0, 0))
base_rev, action, is_copy,
text_modified, props_modified))
found_readable = 1
else:
found_unreadable = 1
# If our caller doesn't want changed-path stuff, and we have
# the info we need to make an authz determination already,
# quit this loop and get on with it.
if (not include_changed_paths) and found_unreadable and found_readable:
break
# Filter unreadable information.
if found_unreadable:
message = None
msg = None
if not found_readable:
author = None
date = None
revs.append([date, author, message, changes])
client.svn_client_log([self.rootpath], optrev, optrev,
1, 0, _log_cb, self.ctx)
return revs[0][0], revs[0][1], revs[0][2], revs[0][3]
# Drop unrequested changes.
if not include_changed_paths:
changes = None
# Add this revision information to the "return" array.
retval.append([date, author, msg, revprops, changes])
optrev = _rev2optrev(rev)
client_log(self.rootpath, optrev, optrev, 1, need_changes, 0,
_log_cb, self.ctx)
return tuple(revs[0])
def _revinfo(self, rev, include_changed_paths=0):
"""Internal-use, cache-friendly revision information harvester."""
# Consult the revinfo cache first. If we don't have cached info,
# or our caller wants changed paths and we don't have those for
# this revision, go do the real work.
rev = self._getrev(rev)
cached_info = self._revinfo_cache.get(rev)
if not cached_info \
or (include_changed_paths and cached_info[4] is None):
cached_info = self._revinfo_fetch(rev, include_changed_paths)
self._revinfo_cache[rev] = cached_info
return cached_info
##--- custom --##
@@ -486,6 +713,7 @@ class RemoteSubversionRepository(vclib.Repository):
try:
results = ra.get_locations(self.ra_session, path, rev, [old_rev])
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.ItemNotFound(path)
raise
@@ -493,23 +721,16 @@ class RemoteSubversionRepository(vclib.Repository):
old_path = results[old_rev]
except KeyError:
raise vclib.ItemNotFound(path)
return _cleanup_path(old_path)
old_path = _cleanup_path(old_path)
old_path_parts = _path_parts(old_path)
# Check access (lying about path types)
if not vclib.check_path_access(self, old_path_parts, vclib.FILE, old_rev):
raise vclib.ItemNotFound(path)
return old_path
def created_rev(self, path, rev):
# NOTE: We can't use svn_client_propget here because the
# interfaces in that layer strip out the properties not meant for
# human consumption (such as svn:entry:committed-rev, which we are
# using here to get the created revision of PATH@REV).
kind = ra.svn_ra_check_path(self.ra_session, path, rev)
if kind == core.svn_node_none:
raise vclib.ItemNotFound(_path_parts(path))
elif kind == core.svn_node_dir:
props = get_directory_props(self.ra_session, path, rev)
elif kind == core.svn_node_file:
fetched_rev, props = ra.svn_ra_get_file(self.ra_session, path, rev, None)
return int(props.get(core.SVN_PROP_ENTRY_COMMITTED_REV,
SVN_INVALID_REVNUM))
lh_rev, c_rev = self._get_last_history_rev(_path_parts(path), rev)
return lh_rev
def last_rev(self, path, peg_revision, limit_revision=None):
"""Given PATH, known to exist in PEG_REVISION, find the youngest
@@ -544,3 +765,33 @@ class RemoteSubversionRepository(vclib.Repository):
else:
peg_revision = mid
return peg_revision, path
def get_symlink_target(self, path_parts, rev):
"""Return the target of the symbolic link versioned at PATH_PARTS
in REV, or None if that object is not a symlink."""
path = self._getpath(path_parts)
path_type = self.itemtype(path_parts, rev) # does auth-check
rev = self._getrev(rev)
url = self._geturl(path)
# Symlinks must be files with the svn:special property set on them
# and with file contents which read "link SOME_PATH".
if path_type != vclib.FILE:
return None
pairs = client.svn_client_proplist2(url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
props = pairs and pairs[0][1] or {}
if not props.has_key(core.SVN_PROP_SPECIAL):
return None
pathspec = ''
### FIXME: We're being a touch sloppy here, first by grabbing the
### whole file and then by checking only the first line
### of it.
fp = SelfCleanFP(cat_to_tempfile(self, path, rev))
pathspec = fp.readline()
fp.close()
if pathspec[:5] != 'link ':
return None
return pathspec[5:]

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -15,15 +15,12 @@
import vclib
import os
import os.path
import stat
import string
import cStringIO
import signal
import shutil
import time
import tempfile
import popen
import re
import urllib
from svn import fs, repos, core, client, delta
@@ -31,19 +28,39 @@ from svn import fs, repos, core, client, delta
if (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_PATCH) < (1, 3, 1):
raise Exception, "Version requirement not met (needs 1.3.1 or better)"
### Pre-1.5 Subversion doesn't have SVN_ERR_CEASE_INVOCATION
try:
_SVN_ERR_CEASE_INVOCATION = core.SVN_ERR_CEASE_INVOCATION
except:
_SVN_ERR_CEASE_INVOCATION = core.SVN_ERR_CANCELLED
### Pre-1.5 SubversionException's might not have the .msg and .apr_err members
def _fix_subversion_exception(e):
if not hasattr(e, 'apr_err'):
e.apr_err = e[1]
if not hasattr(e, 'message'):
e.message = e[0]
### Pre-1.4 Subversion doesn't have svn_path_canonicalize()
def _canonicalize_path(path):
try:
return core.svn_path_canonicalize(path)
except AttributeError:
return path
def _allow_all(root, path, pool):
"""Generic authz_read_func that permits access to all paths"""
return 1
def _path_parts(path):
return filter(None, string.split(path, '/'))
return filter(None, path.split('/'))
def _cleanup_path(path):
"""Return a cleaned-up Subversion filesystem path"""
return string.join(_path_parts(path), '/')
return '/'.join(_path_parts(path))
def _fs_path_join(base, relative):
@@ -97,11 +114,37 @@ def _rev2optrev(rev):
def _rootpath2url(rootpath, path):
rootpath = os.path.abspath(rootpath)
if rootpath and rootpath[0] != '/':
rootpath = '/' + rootpath
drive, rootpath = os.path.splitdrive(rootpath)
if os.sep != '/':
rootpath = string.replace(rootpath, os.sep, '/')
return 'file://' + string.join([rootpath, path], "/")
rootpath = rootpath.replace(os.sep, '/')
rootpath = urllib.quote(rootpath)
path = urllib.quote(path)
if drive:
url = 'file:///' + drive + rootpath + '/' + path
else:
url = 'file://' + rootpath + '/' + path
return _canonicalize_path(url)
# Given a dictionary REVPROPS of revision properties, pull special
# ones out of them and return a 4-tuple containing the log message,
# the author, the date (converted from the date string property), and
# a dictionary of any/all other revprops.
def _split_revprops(revprops):
if not revprops:
return None, None, None, {}
special_props = []
for prop in core.SVN_PROP_REVISION_LOG, \
core.SVN_PROP_REVISION_AUTHOR, \
core.SVN_PROP_REVISION_DATE:
if revprops.has_key(prop):
special_props.append(revprops[prop])
del(revprops[prop])
else:
special_props.append(None)
msg, author, datestr = tuple(special_props)
date = _datestr_to_date(datestr)
return msg, author, date, revprops
def _datestr_to_date(datestr):
@@ -154,7 +197,7 @@ class NodeHistory:
test_path = path
found = 0
while 1:
off = string.rfind(test_path, '/')
off = test_path.rfind('/')
if off < 0:
break
test_path = test_path[0:off]
@@ -167,60 +210,11 @@ class NodeHistory:
return
self.histories.append([revision, _cleanup_path(path)])
if self.limit and len(self.histories) == self.limit:
raise core.SubversionException("", core.SVN_ERR_CEASE_INVOCATION)
raise core.SubversionException("", _SVN_ERR_CEASE_INVOCATION)
def __getitem__(self, idx):
return self.histories[idx]
def _get_history(svnrepos, path, rev, path_type, limit=0, options={}):
rev_paths = []
fsroot = svnrepos._getroot(rev)
show_all_logs = options.get('svn_show_all_dir_logs', 0)
if not show_all_logs:
# See if the path is a file or directory.
kind = fs.check_path(fsroot, path)
if kind is core.svn_node_file:
show_all_logs = 1
# Instantiate a NodeHistory collector object, and use it to collect
# history items for PATH@REV.
history = NodeHistory(svnrepos.fs_ptr, show_all_logs, limit)
try:
repos.svn_repos_history(svnrepos.fs_ptr, path, history.add_history,
1, rev, options.get('svn_cross_copies', 0))
except core.SubversionException, e:
if e.apr_err != core.SVN_ERR_CEASE_INVOCATION:
raise
# Now, iterate over those history items, checking for changes of
# location, pruning as necessitated by authz rules.
for hist_rev, hist_path in history:
path_parts = _path_parts(hist_path)
if not vclib.check_path_access(svnrepos, path_parts, path_type, hist_rev):
break
rev_paths.append([hist_rev, hist_path])
return rev_paths
def _log_helper(svnrepos, path, rev, lockinfo):
rev_root = fs.revision_root(svnrepos.fs_ptr, rev)
# Was this path@rev the target of a copy?
copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path)
# Assemble our LogEntry
date, author, msg, changes = svnrepos.revinfo(rev)
if fs.is_file(rev_root, path):
size = fs.file_length(rev_root, path)
else:
size = None
entry = Revision(rev, date, author, msg, size, lockinfo, path,
copyfrom_path and _cleanup_path(copyfrom_path),
copyfrom_rev)
return entry
def _get_last_history_rev(fsroot, path):
history = fs.node_history(fsroot, path)
history = fs.history_prev(history, 0)
@@ -299,14 +293,15 @@ class FileContentsPipe:
class BlameSource:
def __init__(self, local_url, rev, first_rev):
def __init__(self, local_url, rev, first_rev, include_text, config_dir):
self.idx = -1
self.first_rev = first_rev
self.blame_data = []
self.include_text = include_text
ctx = client.ctx_t()
core.svn_config_ensure(None)
ctx.config = core.svn_config_get_config(None)
ctx = client.svn_client_create_context()
core.svn_config_ensure(config_dir)
ctx.config = core.svn_config_get_config(config_dir)
ctx.auth_baton = core.svn_auth_open([])
try:
### TODO: Is this use of FIRST_REV always what we want? Should we
@@ -314,6 +309,7 @@ class BlameSource:
client.blame2(local_url, _rev2optrev(rev), _rev2optrev(first_rev),
_rev2optrev(rev), self._blame_cb, ctx)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_CLIENT_IS_BINARY_FILE:
raise vclib.NonTextualFileContents
raise
@@ -322,6 +318,8 @@ class BlameSource:
prev_rev = None
if rev > self.first_rev:
prev_rev = rev - 1
if not self.include_text:
text = None
self.blame_data.append(vclib.Annotation(text, line_no + 1, rev,
prev_rev, author, None))
@@ -358,31 +356,14 @@ class LocalSubversionRepository(vclib.Repository):
self.rootpath = rootpath
self.name = name
self.auth = authorizer
self.svn_client_path = utilities.svn or 'svn'
self.diff_cmd = utilities.diff or 'diff'
self.config_dir = config_dir
self.config_dir = config_dir or None
# See if this repository is even viewable, authz-wise.
if not vclib.check_root_access(self):
raise vclib.ReposNotFound(name)
def open(self):
# Register a handler for SIGTERM so we can have a chance to
# cleanup. If ViewVC takes too long to start generating CGI
# output, Apache will grow impatient and SIGTERM it. While we
# don't mind getting told to bail, we want to gracefully close the
# repository before we bail.
def _sigterm_handler(signum, frame, self=self):
sys.exit(-1)
try:
signal.signal(signal.SIGTERM, _sigterm_handler)
except ValueError:
# This is probably "ValueError: signal only works in main
# thread", which will get thrown by the likes of mod_python
# when trying to install a signal handler from a thread that
# isn't the main one. We'll just not care.
pass
# Open the repository and init some other variables.
self.repos = repos.svn_repos_open(self.rootpath)
self.fs_ptr = repos.svn_repos_fs(self.repos)
@@ -390,6 +371,10 @@ class LocalSubversionRepository(vclib.Repository):
self._fsroots = {}
self._revinfo_cache = {}
# See if a universal read access determination can be made.
if self.auth and self.auth.check_universal_access(self.name) == 1:
self.auth = None
def rootname(self):
return self.name
@@ -405,19 +390,14 @@ class LocalSubversionRepository(vclib.Repository):
def itemtype(self, path_parts, rev):
rev = self._getrev(rev)
basepath = self._getpath(path_parts)
kind = fs.check_path(self._getroot(rev), basepath)
pathtype = None
if kind == core.svn_node_dir:
pathtype = vclib.DIR
elif kind == core.svn_node_file:
pathtype = vclib.FILE
else:
pathtype = self._gettype(basepath, rev)
if pathtype is None:
raise vclib.ItemNotFound(path_parts)
if not vclib.check_path_access(self, path_parts, pathtype, rev):
raise vclib.ItemNotFound(path_parts)
return pathtype
def openfile(self, path_parts, rev):
def openfile(self, path_parts, rev, options):
path = self._getpath(path_parts)
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." % path)
@@ -456,7 +436,7 @@ class LocalSubversionRepository(vclib.Repository):
continue
path = self._getpath(entry_path_parts)
entry_rev = _get_last_history_rev(fsroot, path)
date, author, msg, changes = self.revinfo(entry_rev)
date, author, msg, revprops, changes = self._revinfo(entry_rev)
entry.rev = str(entry_rev)
entry.date = date
entry.author = author
@@ -505,20 +485,19 @@ class LocalSubversionRepository(vclib.Repository):
# 'limit' parameter here as numeric cut-off for the depth of our
# history search.
if options.get('svn_latest_log', 0):
revision = _log_helper(self, path, rev, lockinfo)
revision = self._log_helper(path, rev, lockinfo)
if revision:
revision.prev = None
revs.append(revision)
else:
history = _get_history(self, path, rev, path_type,
first + limit, options)
history = self._get_history(path, rev, path_type, first + limit, options)
if len(history) < first:
history = []
if limit:
history = history[first:first+limit]
for hist_rev, hist_path in history:
revision = _log_helper(self, hist_path, hist_rev, lockinfo)
revision = self._log_helper(hist_path, hist_rev, lockinfo)
if revision:
# If we have unreadable copyfrom data, obscure it.
if revision.copy_path is not None:
@@ -539,114 +518,23 @@ class LocalSubversionRepository(vclib.Repository):
fsroot = self._getroot(rev)
return fs.node_proplist(fsroot, path)
def annotate(self, path_parts, rev):
def annotate(self, path_parts, rev, include_text=False):
path = self._getpath(path_parts)
path_type = self.itemtype(path_parts, rev) # does auth-check
if path_type != vclib.FILE:
raise vclib.Error("Path '%s' is not a file." % path)
rev = self._getrev(rev)
fsroot = self._getroot(rev)
history = _get_history(self, path, rev, path_type, 0,
{'svn_cross_copies': 1})
history = self._get_history(path, rev, path_type, 0,
{'svn_cross_copies': 1})
youngest_rev, youngest_path = history[0]
oldest_rev, oldest_path = history[-1]
source = BlameSource(_rootpath2url(self.rootpath, path),
youngest_rev, oldest_rev)
source = BlameSource(_rootpath2url(self.rootpath, path), youngest_rev,
oldest_rev, include_text, self.config_dir)
return source, youngest_rev
def _revinfo_raw(self, rev):
fsroot = self._getroot(rev)
# Get the changes for the revision
editor = repos.ChangeCollector(self.fs_ptr, fsroot)
e_ptr, e_baton = delta.make_editor(editor)
repos.svn_repos_replay(fsroot, e_ptr, e_baton)
changes = editor.get_changes()
changedpaths = {}
# Now get the revision property info. Would use
# editor.get_root_props(), but something is broken there...
revprops = fs.revision_proplist(self.fs_ptr, rev)
msg = revprops.get(core.SVN_PROP_REVISION_LOG)
author = revprops.get(core.SVN_PROP_REVISION_AUTHOR)
datestr = revprops.get(core.SVN_PROP_REVISION_DATE)
# Copy the Subversion changes into a new hash, converting them into
# ChangedPath objects.
found_readable = found_unreadable = 0
for path in changes.keys():
change = changes[path]
if change.path:
change.path = _cleanup_path(change.path)
if change.base_path:
change.base_path = _cleanup_path(change.base_path)
is_copy = 0
if not hasattr(change, 'action'): # new to subversion 1.4.0
action = vclib.MODIFIED
if not change.path:
action = vclib.DELETED
elif change.added:
action = vclib.ADDED
replace_check_path = path
if change.base_path and change.base_rev:
replace_check_path = change.base_path
if changedpaths.has_key(replace_check_path) \
and changedpaths[replace_check_path].action == vclib.DELETED:
action = vclib.REPLACED
else:
if change.action == repos.CHANGE_ACTION_ADD:
action = vclib.ADDED
elif change.action == repos.CHANGE_ACTION_DELETE:
action = vclib.DELETED
elif change.action == repos.CHANGE_ACTION_REPLACE:
action = vclib.REPLACED
else:
action = vclib.MODIFIED
if (action == vclib.ADDED or action == vclib.REPLACED) \
and change.base_path \
and change.base_rev:
is_copy = 1
if change.item_kind == core.svn_node_dir:
pathtype = vclib.DIR
elif change.item_kind == core.svn_node_file:
pathtype = vclib.FILE
else:
pathtype = None
parts = _path_parts(path)
if vclib.check_path_access(self, parts, pathtype, rev):
if is_copy and change.base_path and (change.base_path != path):
parts = _path_parts(change.base_path)
if not vclib.check_path_access(self, parts, pathtype, change.base_rev):
is_copy = 0
change.base_path = None
change.base_rev = None
changedpaths[path] = SVNChangedPath(path, rev, pathtype,
change.base_path,
change.base_rev, action,
is_copy, change.text_changed,
change.prop_changes)
found_readable = 1
else:
found_unreadable = 1
# Return our tuple, auth-filtered: date, author, msg, changes
if found_unreadable:
msg = None
if not found_readable:
author = None
datestr = None
date = _datestr_to_date(datestr)
return date, author, msg, changedpaths.values()
def revinfo(self, rev):
rev = self._getrev(rev)
cached_info = self._revinfo_cache.get(rev)
if not cached_info:
cached_info = self._revinfo_raw(rev)
self._revinfo_cache[rev] = cached_info
return cached_info[0], cached_info[1], cached_info[2], cached_info[3]
return self._revinfo(rev, 1)
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
p1 = self._getpath(path_parts1)
@@ -661,7 +549,7 @@ class LocalSubversionRepository(vclib.Repository):
args = vclib._diff_args(type, options)
def _date_from_rev(rev):
date, author, msg, changes = self.revinfo(rev)
date, author, msg, revprops, changes = self._revinfo(rev)
return date
try:
@@ -671,6 +559,7 @@ class LocalSubversionRepository(vclib.Repository):
info2 = p2, _date_from_rev(r2), r2
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.InvalidRevision
raise
@@ -678,16 +567,266 @@ class LocalSubversionRepository(vclib.Repository):
def isexecutable(self, path_parts, rev):
props = self.itemprops(path_parts, rev) # does authz-check
return props.has_key(core.SVN_PROP_EXECUTABLE)
def filesize(self, path_parts, rev):
path = self._getpath(path_parts)
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." % path)
fsroot = self._getroot(self._getrev(rev))
return fs.file_length(fsroot, path)
##--- helpers ---##
def _revinfo(self, rev, include_changed_paths=0):
"""Internal-use, cache-friendly revision information harvester."""
def _get_changed_paths(fsroot):
"""Return a 3-tuple: found_readable, found_unreadable, changed_paths."""
editor = repos.ChangeCollector(self.fs_ptr, fsroot)
e_ptr, e_baton = delta.make_editor(editor)
repos.svn_repos_replay(fsroot, e_ptr, e_baton)
changedpaths = {}
changes = editor.get_changes()
# Copy the Subversion changes into a new hash, checking
# authorization and converting them into ChangedPath objects.
found_readable = found_unreadable = 0
for path in changes.keys():
change = changes[path]
if change.path:
change.path = _cleanup_path(change.path)
if change.base_path:
change.base_path = _cleanup_path(change.base_path)
is_copy = 0
if not hasattr(change, 'action'): # new to subversion 1.4.0
action = vclib.MODIFIED
if not change.path:
action = vclib.DELETED
elif change.added:
action = vclib.ADDED
replace_check_path = path
if change.base_path and change.base_rev:
replace_check_path = change.base_path
if changedpaths.has_key(replace_check_path) \
and changedpaths[replace_check_path].action == vclib.DELETED:
action = vclib.REPLACED
else:
if change.action == repos.CHANGE_ACTION_ADD:
action = vclib.ADDED
elif change.action == repos.CHANGE_ACTION_DELETE:
action = vclib.DELETED
elif change.action == repos.CHANGE_ACTION_REPLACE:
action = vclib.REPLACED
else:
action = vclib.MODIFIED
if (action == vclib.ADDED or action == vclib.REPLACED) \
and change.base_path \
and change.base_rev:
is_copy = 1
if change.item_kind == core.svn_node_dir:
pathtype = vclib.DIR
elif change.item_kind == core.svn_node_file:
pathtype = vclib.FILE
else:
pathtype = None
parts = _path_parts(path)
if vclib.check_path_access(self, parts, pathtype, rev):
if is_copy and change.base_path and (change.base_path != path):
parts = _path_parts(change.base_path)
if not vclib.check_path_access(self, parts, pathtype,
change.base_rev):
is_copy = 0
change.base_path = None
change.base_rev = None
found_unreadable = 1
changedpaths[path] = SVNChangedPath(path, rev, pathtype,
change.base_path,
change.base_rev, action,
is_copy, change.text_changed,
change.prop_changes)
found_readable = 1
else:
found_unreadable = 1
return found_readable, found_unreadable, changedpaths.values()
def _get_change_copyinfo(fsroot, path, change):
# If we know the copyfrom info, return it...
if hasattr(change, 'copyfrom_known') and change.copyfrom_known:
copyfrom_path = change.copyfrom_path
copyfrom_rev = change.copyfrom_rev
# ...otherwise, if this change could be a copy (that is, it
# contains an add action), query the copyfrom info ...
elif (change.change_kind == fs.path_change_add or
change.change_kind == fs.path_change_replace):
copyfrom_rev, copyfrom_path = fs.copied_from(fsroot, path)
# ...else, there's no copyfrom info.
else:
copyfrom_rev = core.SVN_INVALID_REVNUM
copyfrom_path = None
return copyfrom_path, copyfrom_rev
def _simple_auth_check(fsroot):
"""Return a 2-tuple: found_readable, found_unreadable."""
found_unreadable = found_readable = 0
if hasattr(fs, 'paths_changed2'):
changes = fs.paths_changed2(fsroot)
else:
changes = fs.paths_changed(fsroot)
paths = changes.keys()
for path in paths:
change = changes[path]
pathtype = None
if hasattr(change, 'node_kind'):
if change.node_kind == core.svn_node_file:
pathtype = vclib.FILE
elif change.node_kind == core.svn_node_dir:
pathtype = vclib.DIR
parts = _path_parts(path)
if pathtype is None:
# Figure out the pathtype so we can query the authz subsystem.
if change.change_kind == fs.path_change_delete:
# Deletions are annoying, because they might be underneath
# copies (make their previous location non-trivial).
prev_parts = parts
prev_rev = rev - 1
parent_parts = parts[:-1]
while parent_parts:
parent_path = '/' + self._getpath(parent_parts)
parent_change = changes.get(parent_path)
if not (parent_change and \
(parent_change.change_kind == fs.path_change_add or
parent_change.change_kind == fs.path_change_replace)):
del(parent_parts[-1])
continue
copyfrom_path, copyfrom_rev = \
_get_change_copyinfo(fsroot, parent_path, parent_change)
if copyfrom_path:
prev_rev = copyfrom_rev
prev_parts = _path_parts(copyfrom_path) + \
parts[len(parent_parts):]
break
del(parent_parts[-1])
pathtype = self._gettype(self._getpath(prev_parts), prev_rev)
else:
pathtype = self._gettype(self._getpath(parts), rev)
if vclib.check_path_access(self, parts, pathtype, rev):
found_readable = 1
copyfrom_path, copyfrom_rev = \
_get_change_copyinfo(fsroot, path, change)
if copyfrom_path and copyfrom_path != path:
parts = _path_parts(copyfrom_path)
if not vclib.check_path_access(self, parts, pathtype,
copyfrom_rev):
found_unreadable = 1
else:
found_unreadable = 1
if found_readable and found_unreadable:
break
return found_readable, found_unreadable
def _revinfo_helper(rev, include_changed_paths):
# Get the revision property info. (Would use
# editor.get_root_props(), but something is broken there...)
revprops = fs.revision_proplist(self.fs_ptr, rev)
msg, author, date, revprops = _split_revprops(revprops)
# Optimization: If our caller doesn't care about the changed
# paths, and we don't need them to do authz determinations, let's
# get outta here.
if self.auth is None and not include_changed_paths:
return date, author, msg, revprops, None
# If we get here, then we either need the changed paths because we
# were asked for them, or we need them to do authorization checks.
#
# If we only need them for authorization checks, though, we
# won't bother generating fully populated ChangedPath items (the
# cost is too great).
fsroot = self._getroot(rev)
if include_changed_paths:
found_readable, found_unreadable, changedpaths = \
_get_changed_paths(fsroot)
else:
changedpaths = None
found_readable, found_unreadable = _simple_auth_check(fsroot)
# Filter our metadata where necessary, and return the requested data.
if found_unreadable:
msg = None
if not found_readable:
author = None
date = None
return date, author, msg, revprops, changedpaths
# Consult the revinfo cache first. If we don't have cached info,
# or our caller wants changed paths and we don't have those for
# this revision, go do the real work.
rev = self._getrev(rev)
cached_info = self._revinfo_cache.get(rev)
if not cached_info \
or (include_changed_paths and cached_info[4] is None):
cached_info = _revinfo_helper(rev, include_changed_paths)
self._revinfo_cache[rev] = cached_info
return tuple(cached_info)
def _log_helper(self, path, rev, lockinfo):
rev_root = fs.revision_root(self.fs_ptr, rev)
copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path)
date, author, msg, revprops, changes = self._revinfo(rev)
if fs.is_file(rev_root, path):
size = fs.file_length(rev_root, path)
else:
size = None
return Revision(rev, date, author, msg, size, lockinfo, path,
copyfrom_path and _cleanup_path(copyfrom_path),
copyfrom_rev)
def _get_history(self, path, rev, path_type, limit=0, options={}):
if self.youngest == 0:
return []
rev_paths = []
fsroot = self._getroot(rev)
show_all_logs = options.get('svn_show_all_dir_logs', 0)
if not show_all_logs:
# See if the path is a file or directory.
kind = fs.check_path(fsroot, path)
if kind is core.svn_node_file:
show_all_logs = 1
# Instantiate a NodeHistory collector object, and use it to collect
# history items for PATH@REV.
history = NodeHistory(self.fs_ptr, show_all_logs, limit)
try:
repos.svn_repos_history(self.fs_ptr, path, history.add_history,
1, rev, options.get('svn_cross_copies', 0))
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err != _SVN_ERR_CEASE_INVOCATION:
raise
# Now, iterate over those history items, checking for changes of
# location, pruning as necessitated by authz rules.
for hist_rev, hist_path in history:
path_parts = _path_parts(hist_path)
if not vclib.check_path_access(self, path_parts, path_type, hist_rev):
break
rev_paths.append([hist_rev, hist_path])
return rev_paths
def _getpath(self, path_parts):
return string.join(path_parts, '/')
return '/'.join(path_parts)
def _getrev(self, rev):
if rev is None or rev == 'HEAD':
return self.youngest
try:
if type(rev) == type(''):
while rev[0] == 'r':
rev = rev[1:]
rev = int(rev)
except ValueError:
except:
raise vclib.InvalidRevision(rev)
if (rev < 0) or (rev > self.youngest):
raise vclib.InvalidRevision(rev)
@@ -700,7 +839,20 @@ class LocalSubversionRepository(vclib.Repository):
r = self._fsroots[rev] = fs.revision_root(self.fs_ptr, rev)
return r
##--- custom --##
def _gettype(self, path, rev):
# Similar to itemtype(), but without the authz check. Returns
# None for missing paths.
try:
kind = fs.check_path(self._getroot(rev), path)
except:
return None
if kind == core.svn_node_dir:
return vclib.DIR
if kind == core.svn_node_file:
return vclib.FILE
return None
##--- custom ---##
def get_youngest_revision(self):
return self.youngest
@@ -710,6 +862,7 @@ class LocalSubversionRepository(vclib.Repository):
results = repos.svn_repos_trace_node_locations(self.fs_ptr, path,
rev, [old_rev], _allow_all)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.ItemNotFound(path)
raise
@@ -759,6 +912,7 @@ class LocalSubversionRepository(vclib.Repository):
try:
mid_id = fs.node_id(self._getroot(mid), path)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
cmp = -1
else:
@@ -776,3 +930,32 @@ class LocalSubversionRepository(vclib.Repository):
return peg_revision, path
finally:
pass
def get_symlink_target(self, path_parts, rev):
"""Return the target of the symbolic link versioned at PATH_PARTS
in REV, or None if that object is not a symlink."""
path = self._getpath(path_parts)
rev = self._getrev(rev)
path_type = self.itemtype(path_parts, rev) # does auth-check
fsroot = self._getroot(rev)
# Symlinks must be files with the svn:special property set on them
# and with file contents which read "link SOME_PATH".
if path_type != vclib.FILE:
return None
props = fs.node_proplist(fsroot, path)
if not props.has_key(core.SVN_PROP_SPECIAL):
return None
pathspec = ''
### FIXME: We're being a touch sloppy here, only checking the first line
### of the file.
stream = fs.file_contents(fsroot, path)
try:
pathspec, eof = core.svn_stream_readline(stream, '\n')
finally:
core.svn_stream_close(stream)
if pathspec[:5] != 'link ':
return None
return pathspec[5:]

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -14,7 +14,7 @@
#
# -----------------------------------------------------------------------
import os, sys, traceback, string, thread
import os, sys, traceback, thread
try:
import win32api
except ImportError, e:
@@ -40,9 +40,9 @@ def CommandLine(command, args):
"""Convert an executable path and a sequence of arguments into a command
line that can be passed to CreateProcess"""
cmd = "\"" + string.replace(command, "\"", "\"\"") + "\""
cmd = "\"" + command.replace("\"", "\"\"") + "\""
for arg in args:
cmd = cmd + " \"" + string.replace(arg, "\"", "\"\"") + "\""
cmd = cmd + " \"" + arg.replace("\"", "\"\"") + "\""
return cmd
def CreateProcess(cmd, hStdInput, hStdOutput, hStdError):
@@ -109,13 +109,13 @@ def CreatePipe(readInheritable, writeInheritable):
def File2FileObject(pipe, mode):
"""Make a C stdio file object out of a win32 file handle"""
if string.find(mode, 'r') >= 0:
if mode.find('r') >= 0:
wmode = os.O_RDONLY
elif string.find(mode, 'w') >= 0:
elif mode.find('w') >= 0:
wmode = os.O_WRONLY
if string.find(mode, 'b') >= 0:
if mode.find('b') >= 0:
wmode = wmode | os.O_BINARY
if string.find(mode, 't') >= 0:
if mode.find('t') >= 0:
wmode = wmode | os.O_TEXT
return os.fdopen(msvcrt.open_osfhandle(pipe.Detach(),wmode),mode)

View File

@@ -1,5 +1,5 @@
/*
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/*
# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/*
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,5 +1,5 @@
/*
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC

View File

@@ -1,9 +1,42 @@
RELEASE MANAGEMENT
RELEASE MANAGEMENT
ViewVC rolls releases from release branches associate with each minor
version of the software. For example, the 1.1.0 is rolled from the
1.1.x branch. The same is true for the 1.1.1, 1.1.2, ... releases.
A. Creating Release Branches
============================
Primary ViewVC development occurs on the trunk, with bugfixes and
compatible features being backported to release branches as
appropriate. When, however, the need arises to create a new release
branch, here's the process (M, N, X, and Y below represent integral
major, minor, and patch version numbers, and are not literal):
1. Create the release branch as a copy of the trunk@HEAD (the
lower-case "x" in the branch name is literal):
svn cp -m "Branch for X.Y release stabilization." . ^/branches/X.Y.x
2. On the trunk, update the following files to reflect the new
version which trunk will be progressing towards:
CHANGES: Add stub section for new release.
INSTALL: Update example configuration.
lib/viewvc.py: Update "__version__" value.
docs/upgrading-howto.html: Add stub section for new release.
docs/template-authoring-guide.html: Update to reflect new release.
docs/release-notes/M.N.0.html: Add a new stub file.
Commit these changes:
svn ci -m "Trunk is now progressing toward version M.N."
B. Publishing Releases
======================
There is a script, `tools/make-release', which creates a release
directory and the various archive files that we distribute. All other
steps required to get a ViewVC release out of the door require manual
@@ -13,21 +46,24 @@ follows:
Checkout a working copy of the release branch for the release you
intend to roll, and in that working copy, perform the following steps
(X, Y, and Z below represent integral major, minor, and patch version
numbers, and not literal):
numbers, and are not literal):
1. Review any open bug reports:
http://viewvc.tigris.org/servlets/ProjectIssues
2. Add a new subsection to the file 'docs/upgrading.html' describing
all user visible changes for users of previous releases of ViewVC.
Commit any modifications. NOTE: This step should not be necessary
for patch releases.
2. Ensure that the file 'docs/upgrading.html' describes all user
visible changes for users of previous releases of ViewVC. (Any
changes here should be made on the trunk and backported to the
branch.) NOTE: This step should not be necessary for patch
releases.
3. Verify that copyright years are correct in both the license-1.html
file and the source code.
4. Update and commit the 'CHANGES' file.
4. Update and commit the 'CHANGES' file, using any available crystal
balls or other forward-looking devices to take a stab at the
release date.
5. Test, test, test! There is no automatic testsuite available. So
just run with permuting different `viewvc.conf' settings... and
@@ -38,50 +74,86 @@ numbers, and not literal):
should exactly reflect what you wish to distribute and dub "the
release".
7. Edit the file 'lib/viewvc.py' and remove the "-dev" suffix from
7. Update your release branch working copy to HEAD.
svn up
8. Edit the file 'lib/viewvc.py' and remove the "-dev" suffix from
__version__. The remainder should be of the form "X.Y.Z", where X,
Y, and Z are positive integers. Do NOT commit this change.
Y, and Z are positive integers.
8. Update your working copy to HEAD, and tag the release:
*** Do NOT commit this change. ***
svn update
svn cp -m "Tag the X.Y.Z final release." . \
http://viewvc.tigris.org/svn/viewvc/tags/X.Y.Z
9. "Peg" the contributed templates externals definition to the
current HEAD revision:
9. Go into an empty directory and run the 'make-release' script:
svn pedit svn:externals .
(squeeze "-rBASE_REV", where BASE_REV is the current HEAD revision
number, between 'templates-contrib' and the target URL).
tools/make-release viewvc-X.Y.Z X.Y.Z
*** Do NOT commit this change. ***
10. Verify the archive files:
10. Tag the release:
svn cp -m "Tag the X.Y.Z final release." . ^/tags/X.Y.Z
This will create a copy of the release branch, plus your local
modifications to the svn:externals property and lib/viewvc.py
file, to the tag location.
11. Revert the changes in your working copy.
svn revert -R .
12. Go into an empty directory and run the 'make-release' script:
tools/make-release viewvc-X.Y.Z tags/X.Y.Z
13. Verify the archive files:
- do they have a LICENSE.html file?
- do they have necessary include documentation?
- do they *not* have unnecessary stuff?
- do they install and work correctly?
11. Upload the created archive files (tar.gz and zip) into the Files
14. Upload the created archive files (tar.gz and zip) into the Files
and Documents section of the Tigris.org project, and modify the
CHECKSUMS document there accordingly. Also, drop a copy of the
archive files into the root directory of the viewvc.org website
(unversioned).
CHECKSUMS document there accordingly:
12. On trunk, update the websites (both the viewvc.org/ and www/ ones)
to refer to the new release files, and copy the CHANGES for the
new release into trunk's CHANGES file.
http://viewvc.tigris.org/servlets/ProjectDocumentList?folderID=6004
13. Edit the file 'lib/viewvc.py' again, re-adding the "-dev" suffix
and incrementing the patch number assigned to the __version__
variable, and add a new empty block in the branch's CHANGES file,
and commit:
Also, drop a copy of the archive files into the root directory of
the viewvc.org website (unversioned).
15. Update the Tigris.org website (^/trunk/www/index.html) to refer to
the new release files and commit.
svn ci -m "Bump latest advertised release."
16. Back on the release branch, edit the file 'lib/viewvc.py' again,
incrementing the patch number assigned to the __version__
variable. Add a new empty block in the branch's CHANGES file.
Commit your changes:
svn ci -m "Begin a new release cycle."
14. Edit the Issue Tracker configuration options, adding a new Version
17. Edit the Issue Tracker configuration options, adding a new Version
for the just-released one, and a new Milestone for the next patch
(and possibly, minor or major) release. (For the Milestone sort
key, use a packed integer XXYYZZ: 1.0.3 == 10003, 2.11.4 == 21104.)
15. Write an announcement explaining all the cool new features and
post it to the announce@ list, to the project's News area, and to
other places interested in this sort of stuff, such as Freshmeat
(http://www.freshmeat.net).
http://viewvc.tigris.org/issues/editversions.cgi?component=viewvc&action=add
http://viewvc.tigris.org/issues/editmilestones.cgi?component=viewvc&action=add
18. Send to the announce@ list a message explaining all the cool new
features.
http://viewvc.tigris.org/ds/viewForumSummary.do?dsForumId=4253
19. Post a new release notification at Freecode.
https://freecode.com/projects/viewvc/releases/new
20. Merge CHANGES for this release into the CHANGES file for newer
release lines and commit.

107
notes/root-heirarchy.txt Normal file
View File

@@ -0,0 +1,107 @@
-*- text -*-
This document carries some design thoughts regarding the solution of
Issue #439[1] ("allow svn repositories to reside in web-navigable
subdirectories")
[1] http://viewvc.tigris.org/issues/show_bug.cgi?id=439
INTRODUCTION
============
Many folks have expressed the desire that ViewVC expose its configured
roots at more or less arbitrary virtual paths below the ViewVC root
URL. An example might explain this better.
Say you have a ViewVC instance configured with roots like so:
# path vc
# ------------ ---
root_parents = /var/cvs/old : cvs,
/var/svn/dev : svn,
/var/svn/qa : svn,
and say that each of these root parents has a few roots whose names
begin with the basenames of the parent directory ("dev-tools" lives in
"/var/svn/dev", "old-docs" lives in "/var/cvs/old", etc.)
Today, ViewVC will display all those roots in the "root listing" view
as if they are siblings:
old-docs/
old-src/
dev-build/
dev-libs/
dev-tools/
qa-scripts/
qa-utils/
In other words, any heirarchy which might exist in the on-disk
locations of the roots, or (in the Subversion case) in the
version-control system itself, is flattened.
But sometimes you might want to preserve -- or even introduce -- some
heirarchy in those roots, exposed to the users. For example, you
might wish that instead of a single "root listing" view, ViewVC
instead presented users with a navigable tree constructed from paths
configured by the admin. For example, imagine if each root_parent
item also carried an "exposure path" bit of configuration:
# path vc exposure-path
# ------------ --- -------------
root_parents = /var/cvs/old : cvs : old,
/var/svn/dev : svn : current/dev,
/var/svn/qa : svn : current/qa,
A visit to ViewVC's root URL would then show:
old/
current/
Clicking into "old/", you'd see:
old-docs/
old-src/
Alternatively, clicking into "current/" would show:
dev/
qa/
...and so on.
In fact, you'd have a virtual heirarchy like so:
/
old/
old-docs/ => CVS root at /var/cvs/old/docs
old-src/ => CVS root at /var/cvs/old/src
current/
dev/
dev-build/ => SVN root at /var/svn/dev/dev-build
dev-libs/ => SVN root at /var/svn/dev/dev-libs
dev-tools/ => SVN root at /var/svn/dev/dev-tools
qa/
qa-scripts/ => SVN root at /var/svn/qa/qa-scripts
qa-utils/ => SVN root at /var/svn/qa/qa-utils
CHALLENGES
==========
* Merely tacking on a new "exposure path" item in the definition of
the root_parents, cvs_roots, and svn_roots seems clunky. It also
limits the the granularity of control: you couldn't assign
different locations to the various roots that live within a single
root parent path. Finally, the current code is banking on there
being only a single colon (:) separator (since those might legally
appear in Windows on-disk paths). That might create a bit of a
compatibility annoyance.
* What do you do about root_as_url_component=0? I guess this feature
is just simply unavailable in that case.
* Do you continue to allow cvs_roots and svn_roots members to specify
a root name, or does the root name concept go away entirely in light
of the new exposure path concept?

View File

@@ -0,0 +1,63 @@
[# setup page definitions]
[define page_title]Diff of /[where][end]
[define help_href][docroot]/help_rootview.html[end]
[# end]
[include "include/header.ezt" "diff"]
[include "include/file_header.ezt"]
[if-any diffs]
[for diffs]
[include "include/diff_display.ezt"]
[end]
[end]
<hr style="margin-top:1em;" />
<table cellpadding="10" class="auto">
<tr>
<td>
<form method="get" action="[diff_format_action]">
<div>
[for diff_format_hidden_values]<input type="hidden" name="[diff_format_hidden_values.name]" value="[diff_format_hidden_values.value]"/>[end]
<select name="diff_format" onchange="submit()">
<option value="h" [is diff_format "h"]selected="selected"[end]>Colored Diff</option>
<option value="l" [is diff_format "l"]selected="selected"[end]>Long Colored Diff</option>
<option value="f" [is diff_format "f"]selected="selected"[end]>Full Colored Diff</option>
<option value="u" [is diff_format "u"]selected="selected"[end]>Unidiff</option>
<option value="c" [is diff_format "c"]selected="selected"[end]>Context Diff</option>
<option value="s" [is diff_format "s"]selected="selected"[end]>Side by Side</option>
</select>
<input type="submit" value="Show" />
</div>
</form>
</td>
<td>
[if-any hide_legend]
&nbsp;
[else]
<table style="border:solid gray 1px;" class="auto">
<tr>
<td>Legend:<br />
<table cellspacing="0" cellpadding="1">
<tr>
<td style="text-align:center;" class="vc_diff_remove">Removed lines/characters</td>
<td class="vc_diff_empty">&nbsp;</td>
</tr>
<tr>
<td style="text-align:center;" colspan="2" class="vc_diff_change">Changed lines/characters</td>
</tr>
<tr>
<td class="vc_diff_empty">&nbsp;</td>
<td style="text-align:center;" class="vc_diff_add">Added lines/characters</td>
</tr>
</table>
</td>
</tr>
</table>
[end]
</td>
</tr>
</table>
[include "include/footer.ezt"]

View File

@@ -4,23 +4,31 @@
<thead>
<tr>
<th style="width: 200px" class="vc_header[is sortby "file"]_sort[end]">
<a href="[sortby_file_href]#dirlist">File
[if-any sortby_file_href]<a href="[sortby_file_href]#dirlist">File</a>[else]File[end]
[is sortby "file"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
</a>
</th>
<th style="width: 96px" class="vc_header"></th>
[if-any sortby_rev_href]
<th class="vc_header[is sortby "rev"]_sort[end]">
<a href="[sortby_rev_href]#dirlist">Last Change
<a href="[sortby_rev_href]#dirlist">Last Change</a>
[is sortby "rev"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
</a>
[else]
<th class="vc_header[is sortby "date"]_sort[end]">
[if-any sortby_date_href]<a href="[sortby_date_href]#dirlist">Last Change</a>[else]Last Change[end]
[is sortby "date"]
<img class="vc_sortarrow" alt="[is sortdir "down"](date)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
[end]
</th>
</tr>
</thead>

View File

@@ -4,50 +4,45 @@
<thead>
<tr>
<th class="vc_header[is sortby "file"]_sort[end]" colspan="2">
<a href="[sortby_file_href]#dirlist">File
[if-any sortby_file_href]<a href="[sortby_file_href]#dirlist">File</a>[else]File[end]
[is sortby "file"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
</a>
</th>
<th class="vc_header[is sortby "rev"]_sort[end]">
<a href="[sortby_rev_href]#dirlist">Rev.
[if-any sortby_rev_href]<a href="[sortby_rev_href]#dirlist">Rev.</a>[else]Rev.[end]
[is sortby "rev"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
</a>
</th>
<th class="vc_header[is sortby "date"]_sort[end]">
<a href="[sortby_date_href]#dirlist">Age
[if-any sortby_date_href]<a href="[sortby_date_href]#dirlist">Age</a>[else]Age[end]
[is sortby "date"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
</a>
</th>
<th class="vc_header[is sortby "author"]_sort[end]">
<a href="[sortby_author_href]#dirlist">Author
[if-any sortby_author_href]<a href="[sortby_author_href]#dirlist">Author</a>[else]Author[end]
[is sortby "author"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
</a>
</th>
[is cfg.options.show_logs "1"]
<th class="vc_header[is sortby "log"]_sort[end]">
<a href="[sortby_log_href]#dirlist">Last log entry
[if-any sortby_log_href]<a href="[sortby_log_href]#dirlist">Last log entry</a>[else]Last log entry[end]
[is sortby "log"]
<img class="vc_sortarrow" alt="[is sortdir "down"](rev)[end]"
width="13" height="13"
src="[docroot]/images/[is sortdir "up"]up[else]down[end].png" />
[end]
</a>
</th>
[end]
</tr>

View File

Before

Width:  |  Height:  |  Size: 764 B

After

Width:  |  Height:  |  Size: 764 B

View File

Before

Width:  |  Height:  |  Size: 337 B

After

Width:  |  Height:  |  Size: 337 B

View File

Before

Width:  |  Height:  |  Size: 205 B

After

Width:  |  Height:  |  Size: 205 B

View File

Before

Width:  |  Height:  |  Size: 247 B

After

Width:  |  Height:  |  Size: 247 B

View File

Before

Width:  |  Height:  |  Size: 755 B

After

Width:  |  Height:  |  Size: 755 B

View File

Before

Width:  |  Height:  |  Size: 162 B

After

Width:  |  Height:  |  Size: 162 B

View File

Before

Width:  |  Height:  |  Size: 219 B

After

Width:  |  Height:  |  Size: 219 B

Some files were not shown because too many files have changed in this diff Show More