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

Compare commits

...

173 Commits

Author SHA1 Message Date
cmpilato
71b8440308 Oops. Datestamp CHANGES file.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.19@2886 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-22 19:21:28 +00:00
cmpilato
370c49ad75 Tag the 1.1.19 final release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.19@2884 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-22 19:09:17 +00:00
cmpilato
40e938f123 Merge from trunk r2880:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2883 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-19 20:41:18 +00:00
cmpilato
57ab699677 * CHANGES
Group some related changes.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2882 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-19 20:35:23 +00:00
cmpilato
c4ec79be78 Merge from trunk r2876:
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.

Also:

* CHANGES
  Note these changes.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2879 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-18 14:06:58 +00:00
cmpilato
6f8c5a64b4 Followup to r2877, fixing a syntax error introduce there.
* lib/viewvc.py
  (detect_encoding): Fix syntax error.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2878 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-18 14:03:49 +00:00
cmpilato
f3335bd10c Merge from trunk r2872 and r2873:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2877 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-18 13:57:11 +00:00
cmpilato
f842941cd1 Merge from trunk r2874:
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/branches/1.1.x@2875 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-17 18:18:21 +00:00
cmpilato
d4eedc49b9 Merge from trunk r2870:
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.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2871 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-16 18:18:26 +00:00
cmpilato
24b213e14c Merge from trunk r2867:
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).

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2869 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-16 13:07:02 +00:00
cmpilato
82c0b25d80 Merge from trunk r2866:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2868 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-15 19:54:57 +00:00
cmpilato
8fdb3c6a98 Merge from trunk r2864:
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/branches/1.1.x@2865 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-04-12 15:06:12 +00:00
cmpilato
ea15def7ad Merge from trunk r2862:
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.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2863 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-20 13:22:14 +00:00
cmpilato
d15ff63726 Merged from trunk r2860 (dropping in some more debugging timestamp
points).

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2861 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-18 18:40:04 +00:00
cmpilato
ac2566c7ab Merge from trunk r2856-r2858, whose combined log messages might read:
Finish issue #487 ("Preserve Subversion symlinks in generated
   tarballs") for remote Subversion repositories, too.
   
   * 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.
     (RemoteSubversionRepository.get_symlink_target): New function.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2859 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-04 20:56:05 +00:00
cmpilato
41fdf2b86d Merge from trunk r2853-r2855:
Finish issue #487 ("Preserve Subversion symlinks in generated tarballs")
   
   * 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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2856 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-03-04 19:41:45 +00:00
cmpilato
a878d93992 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2850 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-28 19:27:40 +00:00
cmpilato
dae4abebaf * CHANGES: Minor wording tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2847 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-28 19:12:17 +00:00
cmpilato
dba173ca38 Signal intent to release 1.1.18 today.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2846 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-28 19:11:20 +00:00
cmpilato
5e18bf0aa4 Merge from trunk r2841-r2844, all of which were commits-db related changes.
* bin/make-database
     Add a --port option and handling thereof, passing the value off to
     'mysql'.
   
     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.
   
     Use "ENGINE=MyISAM" rather than "TYPE=MyISAM" throughout.  The
     latter syntax was deprecated in MySQL 5.0 and dropped in 5.5.
   
   * lib/cvsdb.py
     (CheckinDatabase.CheckCommit, CheckinDatabase.PurgeRepository):
       Name columns in SELECT statement rather than using '*'.

Also:

* CHANGES:
  Note these changes.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2845 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-26 17:04:10 +00:00
cmpilato
41809c22bf Merge from trunk r2839:
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.

Also:

* CHANGES:
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2840 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-25 20:38:09 +00:00
cmpilato
850f4dd0eb Merge from trunk r2837:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2838 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-25 20:04:19 +00:00
cmpilato
7eace69bfb Merge from trunk r2832, whose log message said a little sum'thin' like
this:

   * 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/branches/1.1.x@2833 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-02-22 16:36:21 +00:00
cmpilato
907d9eddd2 Correct a royally botched CHANGES entry.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2829 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-01-31 19:49:41 +00:00
cmpilato
02eefc07a5 Merge from trunk r2827, described as follows:
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.

Also:

* CHANGES:
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2828 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-01-29 16:00:02 +00:00
cmpilato
ddd96672b0 Bump copyright years. (Merged r2820 from ^/trunk.)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2821 8cb11bc2-c004-0410-86c3-e597b4017df7
2013-01-04 19:04:36 +00:00
cmpilato
0fc67216d5 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2813 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-25 14:07:05 +00:00
cmpilato
167a2a041b Merge from trunk r2808, whose log message read like so:
Fix issue #516 ("Regression: UnboundLocalError: local variable
   'log_pagestart' referenced before assignment").
   
   * lib/viewvc.py
     (view_log): Initialize the 'log_pagestart' variable.

Also:

* CHANGES
  Note this change, and plan on a 1.1.17 release today.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2809 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-25 13:48:46 +00:00
cmpilato
c4483d0501 Doh! Forgot to peg the release date for 1.1.16.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2805 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 18:46:08 +00:00
cmpilato
d620bc13e7 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2803 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 17:37:47 +00:00
cmpilato
e3756ae365 Merge from trunk r2796, which fixed issue #514 (Simple file view has
page title with "Annotation of:").

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2797 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 17:01:40 +00:00
cmpilato
77ad38cbdb * CHANGES
Note another change made on the branch.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2795 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 14:40:32 +00:00
cmpilato
7feecdd512 Merge from trunk r2791 and r2792, which did the following:
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>

   * conf/viewvc.conf.dist
     Show the default value of 'hr_funout' as 1 (which matches the
     programmatic default).

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2793 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 13:29:49 +00:00
cmpilato
a087c06a88 Merge from trunk r2788, whose log message read like so:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2789 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-03 14:24:49 +00:00
cmpilato
acce6a556d Merge from trunk r2784, whose log message read like so:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2785 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-09-05 14:55:05 +00:00
cmpilato
4670019d3a Merge from trunk r2781.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2782 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-22 20:23:27 +00:00
cmpilato
9ce7372e1a Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2778 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-22 18:53:40 +00:00
cmpilato
e1959ac2e5 Rolling 1.1.15 today.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2775 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-22 18:41:47 +00:00
cmpilato
cb38ccc929 Merge from trunk r2770 (more ra_svn improvements).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2773 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-19 18:50:01 +00:00
cmpilato
f2b82132c2 Merge from trunk r2771, whose log message read like so:
* 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>

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2772 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-19 18:48:01 +00:00
cmpilato
5caf3a4437 Merge r2767 and r2768 from trunk (bugfixes to recent commits).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2769 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 01:40:03 +00:00
cmpilato
06ee4df42d Merge from trunk r2765, whose log message read like so:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2766 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 01:15:50 +00:00
cmpilato
0a6b13145e Merge from trunk r2763, which did somethin' a little bit like this:
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/branches/1.1.x@2764 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 00:52:29 +00:00
cmpilato
65d3568c92 Merge from trunk r2758, whose log message read like so:
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.

Also:

* CHANGES:
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2762 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 00:25:33 +00:00
cmpilato
de5b147a6f Merge the fixes for issue #353 ("Remote Subversion repositories not
fully honoring authz rules") from trunk.  This merges r2755, r2756,
r2757, r2759, and r2760, which see for detail log revision information.

Also:

* CHANGES: Note the changes this offers.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2761 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 00:03:57 +00:00
cmpilato
eb6b575701 Merge from trunk r2753, which contains some release process doc tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2754 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 13:06:43 +00:00
cmpilato
8d82b6f0d6 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2751 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 12:55:19 +00:00
cmpilato
5a51470cbc Let's try to release 1.1.14 today.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2748 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 12:44:11 +00:00
cmpilato
acc0783468 Merge from trunk r2746, whose log message reads like so:
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.

Also:

* CHANGES:
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2747 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 11:16:18 +00:00
cmpilato
c91325d40a Merge from trunk r2740 and r2741, which provide the fix for issue #506
("Log message tokenization results could be reused for better
performance").  See log messages for those revision for change
details.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2742 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-04-17 17:54:45 +00:00
cmpilato
ca1bd67b5d Merge from trunk r2738, whose log message read like so:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2739 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-03-28 14:29:20 +00:00
cmpilato
f1e7ef42d6 Merge from trunk r2735 and r2736, which see for details. (I'm feeling lazy.)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2737 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-02-07 21:52:48 +00:00
cmpilato
8b7eae7f14 Merge from trunk r2731 and r2733, whose combined log messages might
have read something like this:

   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
     (_canonicalize_path): New helper function.
     (_rootpath2url): Canonicalize URLs for use with Subversion.

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

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2734 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-02-01 19:49:22 +00:00
cmpilato
6f3d9a3a00 Merge some notes tweaks from ^/trunk:r2729.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2732 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-02-01 19:27:51 +00:00
cmpilato
1d7307c09b Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2728 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 18:53:09 +00:00
cmpilato
22d1e72c66 Let's shoot for a 1.1.13 release today, shall we?
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2724 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 18:44:16 +00:00
cmpilato
0e7d4061ed Bump copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2723 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 18:40:36 +00:00
cmpilato
56dd2dcf28 Bump copyright years. (Merged /trunk:r2716)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2717 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 16:42:49 +00:00
cmpilato
5932f24a68 Merge from trunk r2713 (disallowing relative rootpaths) and r2714
(fixing -- no, really fixing this time -- path-to-url calculation for
annotations on Windows).

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2715 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-06 22:01:36 +00:00
cmpilato
161421a20f Merge from trunk r2709.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2710 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-03 20:08:39 +00:00
cmpilato
2a297c5361 Merge from trunk r2707, which at some point in time carried a log
message that read like so:

   * 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/branches/1.1.x@2708 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-03 15:22:31 +00:00
cmpilato
f43117b10f Merge from trunk r2702, whose log message read like so:
* 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/branches/1.1.x@2703 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-12 20:07:58 +00:00
cmpilato
a025237f7e Tweak CHANGES line in light of r2700.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2701 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-06 14:50:02 +00:00
cmpilato
228db2fadb Merge from trunk r2679 and r2699, which is the reintroduction of the
issue #495 ("Syntax highlight/colorize scripts without extensions")
feature, plus a fix to help the prefer_markup flag get set more
accurately for the markup and annotate views.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2700 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-06 14:48:17 +00:00
cmpilato
0cd26cc79f Merge from trunk r2688, whose log message read like so:
* lib/viewvc.py
     (markup_or_annotate): Don't show annotation warnings for markup
       views of images.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2689 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 18:36:32 +00:00
cmpilato
b808d5d1e8 Merge from trunk r2686.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2687 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 18:30:31 +00:00
cmpilato
f843c054b7 Merge from trunk r2684, whose log message read like so:
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).

Also:

* CHANGES
  Note that at this point, the lexer guessing stuff added in 1.1.12 has
  been effectively removed.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2685 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 17:55:43 +00:00
cmpilato
cf06a971e8 Merge from trunk r2680 and r2681, whose combined log message might
read something like this:

   * 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 isn't text-y and have already failed to get a matching lexer.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2682 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-29 15:40:59 +00:00
cmpilato
bc8f3bdd4f Merge from trunk r2674, whose log message read like so:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2675 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-14 21:34:14 +00:00
cmpilato
a6fcab67b0 Merge from trunk r2671, whose log message went a little something like this:
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.)

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2672 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-14 20:16:01 +00:00
cmpilato
4069208316 Merge from trunk r2668 (a doc tweak).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2669 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-03 14:44:33 +00:00
cmpilato
cfaa30b40f Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2667 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-03 14:40:51 +00:00
cmpilato
97c5a82b7b Let's release 1.1.12 today.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2664 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-03 14:27:51 +00:00
cmpilato
21bd391d62 Document a missing template data item (patch_href).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2663 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-03 14:22:25 +00:00
cmpilato
9d1476ef1d Merge from trunk r2657, whose log message read like so:
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.)

Also:

* CHANGES:
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2658 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-31 21:42:38 +00:00
cmpilato
151fcd05e6 Merge from trunk r2653, which did the following:
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/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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2654 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-28 19:30:36 +00:00
cmpilato
22400ddcfb * docs/template-authoring-guide.html
Update <title> and <h1> to reflect that this document is for ViewVC
  1.1, not 1.0.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2636 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-21 14:02:23 +00:00
cmpilato
ae55530edc Record the change made in r2630.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2631 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-17 20:06:02 +00:00
cmpilato
112700a12c * lib/sapi.py
(WsgiServer.escape): Remove as unnecessary and, indeed, incorrect
    per issue #454.

Reported by: Ennio Zarlenga <wzzxo{__AT__}yahoo.fr>


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2630 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-17 20:04:07 +00:00
cmpilato
cecffecf39 Merge from trunk r2609 (fixed pointer to Subversion website).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2610 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-10-04 15:00:01 +00:00
cmpilato
bdac8697fe Merge from trunk r2599:
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/branches/1.1.x@2600 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-09-30 19:11:27 +00:00
cmpilato
3d9404b67d Merge from trunk r2597, whose log message read like so:
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.)

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2598 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-09-30 15:50:31 +00:00
cmpilato
7c50f55153 Merge from trunk r2593:
* 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/branches/1.1.x@2594 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-09-14 17:54:10 +00:00
cmpilato
368e4dc360 Merge from trunk r2591, which added to viewvc.conf.dist descriptions
of the various optionally "allowed_views".


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2592 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-09-07 17:44:18 +00:00
cmpilato
964d8bb5f9 Merge from trunk r2588, whose log message went a little som'thin' like this:
* 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/branches/1.1.x@2589 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-07-01 17:58:19 +00:00
cmpilato
846e8e46c5 Merge from trunk r2586, whose log message read like so:
(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/branches/1.1.x@2587 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-06-23 15:25:59 +00:00
cmpilato
75c719bcde Merge from trunk r2405:
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.)
   
and r2584:

   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>

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2585 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-06-23 15:14:57 +00:00
cmpilato
fc8793bf15 Merge from trunk r2582 which contained only doc tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2583 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-06-14 13:31:35 +00:00
cmpilato
65935c40fd Merge from trunk r2580, whose log message read like so:
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 '$'

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2581 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-06-06 20:05:27 +00:00
cmpilato
a30d0b44cc Merge from trunk r2577, whose log message read like so:
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.

Also:

* CHANGES
  Record this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2578 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-27 19:00:20 +00:00
cmpilato
fded8462d2 Add placeholder for 1.1.12 changes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2576 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-17 12:50:37 +00:00
cmpilato
b1095ac763 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2575 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-17 12:38:54 +00:00
cmpilato
e54399a169 Let's try to roll 1.1.11 today, shall we?
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2572 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-17 12:23:20 +00:00
cmpilato
4cc0db75be Merge from trunk r2569 and r2570, which just updated some copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2571 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-17 12:21:27 +00:00
cmpilato
74a9cbb2a0 Minor wording, spelling, and ordering tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2568 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-17 11:49:31 +00:00
cmpilato
390e337a8e * bin/standalone.py
(main): Was cli().  Caller(s) updated.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2567 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-12 19:01:48 +00:00
cmpilato
ecdac77d5f Merge from trunk r2565, whose modified-per-conflict-resolution log
message might read something like this:

   Add '--help' option to standalone.py.
   
   * bin/standalone.py
     (usage, badusage): New functions.
     (cli): 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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2566 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-12 19:01:05 +00:00
cmpilato
79158c2ee7 Merge from trunk r2559-2563, which is a bunch of tweaks to the
standalone.py usage message and options handling.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2564 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-12 13:50:05 +00:00
cmpilato
96fdfbdbcf Merge from trunk r2557, whose log message read like so:
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).

Also:

* CHANGES
  Record this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2558 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-05-05 14:39:22 +00:00
cmpilato
361538da21 Merge from trunk r2555, whose log message read like so:
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>

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2556 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-21 16:06:23 +00:00
cmpilato
6250d4134b Merge some spelling fixes from trunk's r2553 here.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2554 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-20 15:02:08 +00:00
cmpilato
8589949521 Merge from trunk r2550 and r2551, whose combined log messages might
read like so:

   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.GetCommitsTable): New helper.
     (CheckinDatabase.AddCommit, CheckinDatabase.CheckCommit,
      CheckinDatabase.sql_delete): Use new GetCommitsTable() helper
       instead of hard-coding the version-specific selection of the
       commits table.
     (CheckinDatabase.CreateSQLQueryString): Add 'detect_leftover'
       parameter, used internally to check for a reached query limit.
       Also, use new GetCommitsTable() helper.
     (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.
   
   * conf/viewvc.conf.dist
     (row_limit, rss_row_limit): Make it clear what exactly is getting
       limited here.

Also:

* CHANGES:
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2552 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-20 14:56:39 +00:00
cmpilato
f12e262fa5 Merge from trunk r2547, whose log message read like so:
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.

Also:

* CHANGES
  Note this change, referring to it as a 'security fix' due to the
  ramifications of allowing folks to query your potentially monstrous
  database while ignoring your configured response set limits.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2548 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-19 20:44:05 +00:00
cmpilato
f379070697 Merge from trunk r2545, whose log message read something like this:
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().

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2546 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-01 17:02:07 +00:00
cmpilato
d47fc0ff3b Merge from trunk r2543 (with some tweaks to use the 'string' module
functions), whose log message looks like so:

  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.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2544 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-04-01 14:43:01 +00:00
cmpilato
f2b6f0ba86 Merge from trunk r2541, whose log message read like so:
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().

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2542 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-29 16:41:09 +00:00
cmpilato
ddbe150be1 Merge from trunk r2538, which did a little something like this:
* 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.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2539 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-15 20:42:42 +00:00
cmpilato
6b5ed7c857 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2537 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-15 16:37:38 +00:00
cmpilato
391f7d8237 Let's get 1.1.10 out today.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2534 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-15 14:57:50 +00:00
cmpilato
141cf5ff10 * CHANGES
Tweak description of a change to not leave room for interpretation that
  the bug was a security vulnerability.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2533 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-15 14:56:15 +00:00
cmpilato
bfe148eb45 Merge from trunk r2531, whose log message read like so:
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.

Also:

* CHANGES
  Note this change, sadly confessing to introducing a regression.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2532 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-03-11 20:22:58 +00:00
cmpilato
6fb28f2198 Add some helpful URLs to the release instructions. (Merged /trunk:2529.)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2530 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 20:02:37 +00:00
cmpilato
02cc53d34b Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2528 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 19:56:28 +00:00
cmpilato
5928918da4 Merge from trunk r2524, a bump in copyright years.
(Also includes an accidental commit to CHANGES naming today as the
1.1.9 release date.  Which sounds good.  What say you?)

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2525 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 19:27:50 +00:00
cmpilato
2e9f84427c Merge from trunk r2522:
* conf/viewvc.conf.dist (log_pagesextra): Set value to match defaults.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2523 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 18:28:55 +00:00
cmpilato
df599031a0 Merge from trunk r2519:
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/branches/1.1.x@2520 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 16:57:47 +00:00
cmpilato
6311c93298 Merge from trunk r2517, whose log message might be interpreted like so:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2518 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-18 15:29:11 +00:00
cmpilato
b982cccbcd Merge from trunk r2514, whose log message read like so:
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.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2516 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-17 16:12:54 +00:00
cmpilato
33b8224714 Minor wording tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2515 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-17 16:11:45 +00:00
cmpilato
2f05f570b1 Merge from trunk r2512 which looked a little somethin' like this:
* 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.

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2513 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-16 20:34:37 +00:00
cmpilato
d737657e1d Merge from trunk r2509, whose log message read like so:
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.)

Also:

* CHANGES:
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2510 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-14 16:00:40 +00:00
cmpilato
3d5294635e Merge from trunk r2507, which is just some code re-org business:
* 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/branches/1.1.x@2508 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-11 21:18:11 +00:00
cmpilato
75c3fc2346 Merge from trunk r2505, which did something like this:
* lib/vcauth/svnauthz/__init__.py
     (ViewVCAuthorizer._get_paths_for_root): Trap and return a cleaned-up
       error when the authzfile isn't parse-able.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2506 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-11 16:57:52 +00:00
cmpilato
80bce159af Clarify that Python 3.x is not yet supported. (Merged /trunk:r2502)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2503 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-02-07 14:49:01 +00:00
cmpilato
7ff9b84ee1 Merge from trunk r2500, whose log message read like so:
* lib/vclib/svn/svn_ra.py
     (client_log): Check for the correct exception.
   
   Reported by: Phil Jeary <Phil.Jeary@dovetailservices.com>

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2501 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-01-04 19:28:10 +00:00
cmpilato
b6acd3c114 Merge from trunk r2498, whose log message read like so (after I corrected it. twice.):
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.

Also:

* CHANGES
  Record this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2499 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-09 17:14:20 +00:00
cmpilato
fa5d40caa3 Merge from trunk r2496, whose log message read like so:
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.

Also:

* CHANGES
  Record this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2497 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-09 16:22:05 +00:00
cmpilato
a9cbd4c6a6 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2488 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-02 20:59:12 +00:00
cmpilato
882cdaa46b Let's get 1.1.8 out today, m'kay?
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2485 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-02 20:46:06 +00:00
cmpilato
58e64cb28d Minor tweak to the release process notes. (Merged /trunk:2483)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2484 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-02 20:38:43 +00:00
cmpilato
779e67653b Re-do a bit of conflict resolution from r2479 to reduce release-to-release
delta.

* bin/standalone.py
  (main): Describe --htpasswd-file before --gui now.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2482 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-12-02 20:34:29 +00:00
cmpilato
0381a772b7 Merge from trunk r2480, whose log message read like so:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2481 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-30 21:13:17 +00:00
cmpilato
048b2c8033 Merge from trunk r2478, whose log message read like so at the time:
* 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.

Also:

* CHANGES
  Update a change item to reflect the new state of code 'round these parts.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2479 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-30 18:26:44 +00:00
cmpilato
e354ab302c Merge from trunk r2476, whose log message read a bit like this:
* 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/branches/1.1.x@2477 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-30 16:44:16 +00:00
cmpilato
5f931c6cf1 Merge from trunk r2473, whose log message read like so:
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.

Also:

* CHANGES
  Note this fix, and tweak the announcement of Basic authn support for
  standalone.py to note "Unix only".


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2474 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-11-16 16:16:09 +00:00
cmpilato
576837351a Merge from trunk r2471, whose log message read like so:
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().

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2472 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-10-05 15:04:32 +00:00
cmpilato
6084ccf877 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2468 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 18:49:00 +00:00
cmpilato
514da91629 Let's release 1.1.7 today, shall we?
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2465 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 18:21:15 +00:00
cmpilato
d2c8bb9878 Merge from trunk r2463, which bumps some copyright years.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2464 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 18:20:01 +00:00
cmpilato
fe274e9b9c Merge from trunk r2458, r2459, and r2460, whose combined log messages
might read like so:

   Allow users to tell the query script where ViewVC is located while
   still generating the same defaults as before.
   
   Also, fix the query script's WSGI and FastCGI deployments.
   
   * 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.
   
   * 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.
   
   * 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.

Also:

* CHANGES
  Note these changes.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2462 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 15:31:52 +00:00
cmpilato
6603a67a91 * CHANGES
Tweak change description.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2461 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-09 15:27:55 +00:00
cmpilato
b50de1c92e Merge from trunk r2454 and r2455, whose log messages contained the
following:

   * lib/vclib/svn/svn_repos.py
     (_split_revprops): Special-case the handling of empty input.
   
   * lib/viewvc.py
     (_re_rewrite_svnrevref): Wrap the regexp with word boundaries.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2456 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-08 18:28:25 +00:00
cmpilato
dfd8dc82f6 Merge from trunk r2452, some release procedure tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2453 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-08 17:14:44 +00:00
cmpilato
1a942d1062 Merge from trunk r2446.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2449 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-07 19:22:26 +00:00
cmpilato
289a78eb99 Merge from trunk r2444, whose log message read like so:
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.)
   
Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2447 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-07 19:20:22 +00:00
cmpilato
2897763020 Merge some typo fixes from trunk (r2441, r2442).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2443 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-07 15:41:48 +00:00
cmpilato
47886423e5 Merge from trunk r2439, whose log message said a little somethin' like
this:

   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().

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2440 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-07 15:20:10 +00:00
cmpilato
0890821839 Merge from trunk r2435, whose log message contained the following:
* lib/vclib/svn/svn_repos.py
     (BlameSource.__init__): Use client.create_context() instead of
       client.ctx_t() to avoid "NULL proxy object pool" error.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2436 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-09-03 16:21:25 +00:00
cmpilato
2903389d3e Merge from trunk r2430, whose log message rock opera production had
the following lyrics:

   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().

Also:

* CHANGES
  Record this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2431 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-08-25 20:18:46 +00:00
cmpilato
5bdea369e8 Merge from trunk r2428, whose log message bits were aligned in a
manner roughly similar to the following:

   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.

Also:

* CHANGES
  Note this change.

(And yes, I'm running out of interesting ways to compose log messages
for merged changes.)


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2429 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-08-23 20:29:53 +00:00
cmpilato
3699e8e0c7 Merge from trunk r2426, whose log message read like so:
* 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/branches/1.1.x@2427 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-08-19 04:19:55 +00:00
cmpilato
55c0b30bc0 Merge from trunk r2424, whose log message read like so:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2425 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-08-13 17:50:42 +00:00
cmpilato
6fd321529a Merge from trunk r2421, whose log message read thusly:
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().

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2422 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-08-13 16:20:11 +00:00
cmpilato
531877db4a Merge from trunk r2414, whose log message read thusly:
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.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2415 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-07-15 20:56:50 +00:00
cmpilato
51df003d8e Merge from trunk r2411, whose log message leans a little this way:
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.

Also:

* CHANGES
  Note the fix made in this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2412 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-07-15 20:24:45 +00:00
cmpilato
7d05859d7e Merge from trunk r2407 and r2408, which are mostly just some formatting
changes for standalone.py.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2410 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-07-15 19:11:30 +00:00
cmpilato
2e48cd2ba1 Merge from trunk r2403, whose log message read like so:
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>

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2404 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-17 20:09:06 +00:00
cmpilato
267b61f347 Merge from trunk r2401, whose log message read thusly:
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.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2402 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-15 15:20:50 +00:00
cmpilato
7f53d5cc3c Merge from trunk r2399, whose log message read like so:
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/branches/1.1.x@2400 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-03 19:43:43 +00:00
cmpilato
af591ed9f7 Minor process and verbiage tweak. (Merge from trunk r2393.)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2394 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 14:38:23 +00:00
cmpilato
17492a3856 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2389 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 13:59:23 +00:00
cmpilato
e990ff9f2f And we might as well release 1.1.6 today, too.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2387 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 13:24:27 +00:00
cmpilato
d832930c8d Shorten the update/copy instructions. (Merge r2381 from /trunk.)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2382 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-06-02 12:51:07 +00:00
cmpilato
8a0c66c7cf Merge from trunk r2379, whose log message read like so:
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.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2380 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-20 19:43:32 +00:00
cmpilato
292c2d7ce0 Merge from trunk r2376-r2377, whose log messages read like so:
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.

   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.

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2378 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-20 19:12:26 +00:00
cmpilato
16937af30f Merge from trunk r2374, whose log message read like so:
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>

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2375 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-20 15:08:46 +00:00
cmpilato
d067d49afa Merge from trunk r2372, some rcsparse optimizations.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2373 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-20 14:58:14 +00:00
cmpilato
132a01a88f Merge from trunk r2369, whose log message read like so:
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>

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2370 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-06 13:53:46 +00:00
cmpilato
4db5103ff1 Merge from trunk r2367, some INSTALL notes.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2368 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-05 15:26:44 +00:00
cmpilato
41943f67c5 Merge from trunk r2271, whose log message read thusly:
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>

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2366 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-05-04 17:23:06 +00:00
cmpilato
1f37623526 Lose bogus MIME type in favicon link tag. (Merge r2364 from trunk.)
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2365 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-04-21 19:08:14 +00:00
cmpilato
7e92babad8 Merge from trunk r2362, whose log message read like so:
* 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/branches/1.1.x@2363 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-04-14 17:19:28 +00:00
cmpilato
97e45101d9 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2356 8cb11bc2-c004-0410-86c3-e597b4017df7
2010-03-29 15:51:14 +00:00
69 changed files with 3617 additions and 1430 deletions

124
CHANGES
View File

@@ -1,3 +1,127 @@
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 Basic authentication support to standalone.py (Unix only) (issue #49)
* fix obscure "unexpected NULL parent pool" Subversion bindings error
* enable path info / link display in remote Subversion root revision view
* fix vhost name case handling inconsistency (issue #466)
* use svn:mime-type property charset param as encoding hint
* markup Subversion revision references in log messages (issue #313)
* add rudimentary support for FastCGI-based deployments (issue #464)
* fix query script WSGI deployment
* add configuration to fix query script cross-linking to ViewVC
Version 1.1.6 (released 02-Jun-2010)
* add rudimentary support for WSGI-based deployments (issue #397)
* 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

105
INSTALL
View File

@@ -19,7 +19,7 @@ Congratulations on getting this far. :-)
For CVS Support:
* Python 1.5.2 or later
* Python 1.5.2 or later (sorry, no 3.x support yet)
(http://www.python.org/)
* RCS, Revision Control System
(http://www.cs.purdue.edu/homes/trinkle/RCS/)
@@ -30,11 +30,11 @@ Congratulations on getting this far. :-)
For Subversion Support:
* Python 2.0 or later
* Python 2.0 or later (sorry, no 3.x support yet)
(http://www.python.org/)
* Subversion, Version Control System, 1.3.1 or later
(binary installation and Python bindings)
(http://subversion.tigris.org/)
(http://subversion.apache.org/)
Optional:
@@ -176,7 +176,24 @@ APACHE CONFIGURATION
or /etc/local. Use the vendor documentation or the find utility if
in doubt.
2) Configure Apache to expose ViewVC to users at the URL of your choice.
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:
@@ -187,13 +204,13 @@ APACHE CONFIGURATION
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
----------------------------------------
METHOD B: CGI mode in cgi-bin directory
@@ -201,6 +218,10 @@ APACHE CONFIGURATION
Copy the CGI scripts from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/cgi/*.cgi
to the /cgi-bin/ directory configured in your httpd.conf file.
You can override configuration file location using:
SetEnv VIEWVC_CONF_PATHNAME /etc/viewvc.conf
------------------------------------------
METHOD C: CGI mode in ExecCGI'd directory
@@ -210,12 +231,12 @@ APACHE CONFIGURATION
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:
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.
------------------------------------------
METHOD D: Using mod_python (if installed)
@@ -227,17 +248,60 @@ APACHE CONFIGURATION
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.
3) Restart Apache.
----------------------------------------
METHOD E: Using mod_wsgi (if installed)
----------------------------------------
Copy the Python scripts file from
<VIEWVC_INSTALLATION_DIRECTORY>/bin/mod_python/
to the directory of your choosing. Modify httpd.conf with the
following directives:
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".
WSGIScriptAlias /viewvc <VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/viewvc.wsgi
WSGIScriptAlias /query <VIEWVC_INSTALLATION_DIRECTORY>/bin/wsgi/query.wsgi
You'll probably also need the following directive because of the
not-quite-sanctioned way that ViewVC manipulates Python objects.
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] Add access control.
@@ -260,7 +324,14 @@ APACHE CONFIGURATION
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.
5) Restart Apache.
The commands to do this vary. "httpd -k restart" and "apache -k
restart" are two common variants. On RedHat Linux it is done using
the command "/sbin/service httpd restart" and on SuSE Linux it is
done with "rcapache restart". Other systems use "apachectl restart".
6) Optional: Protect your ViewVC instance from server-whacking webcrawlers.
As ViewVC is a web-based application which each page containing various
links to other pages and views, you can expect your server's performance

View File

@@ -15,7 +15,7 @@
<blockquote>
<p><strong>Copyright &copy; 1999-2010 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
@@ -61,6 +61,9 @@
<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
@@ -54,4 +54,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

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

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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2009 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,7 @@ CREATE TABLE branches (
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 +63,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 +72,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 +80,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 +88,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 +96,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 +104,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,7 +118,7 @@ CREATE TABLE tags (
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
) TYPE=MyISAM;
) ENGINE=MyISAM;
"""
## ------------------------------------------------------------------------
@@ -132,7 +132,7 @@ CREATE TABLE branches (
branch varchar(64) binary DEFAULT '' NOT NULL,
PRIMARY KEY (id),
UNIQUE branch (branch)
) TYPE=MyISAM;
) ENGINE=MyISAM;
DROP TABLE IF EXISTS commits;
CREATE TABLE commits (
@@ -156,7 +156,7 @@ CREATE TABLE commits (
KEY fileid (fileid),
KEY branchid (branchid),
KEY descid (descid)
) TYPE=MyISAM;
) ENGINE=MyISAM;
DROP TABLE IF EXISTS descs;
CREATE TABLE descs (
@@ -165,7 +165,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 (
@@ -173,7 +173,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 (
@@ -181,7 +181,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 (
@@ -189,7 +189,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 (
@@ -197,7 +197,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 (
@@ -211,7 +211,7 @@ CREATE TABLE tags (
KEY dirid (dirid),
KEY fileid (fileid),
KEY branchid (branchid)
) TYPE=MyISAM;
) ENGINE=MyISAM;
DROP TABLE IF EXISTS metadata;
CREATE TABLE metadata (
@@ -219,7 +219,7 @@ CREATE TABLE metadata (
value text,
PRIMARY KEY (name),
UNIQUE name (name)
) TYPE=MyISAM;
) ENGINE=MyISAM;
INSERT INTO metadata (name, value) VALUES ('version', '1');
"""
@@ -245,6 +245,10 @@ 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.
@@ -253,7 +257,8 @@ Options:
--help Show this usage message.
--hostname=ARG Use ARG as the hostname for the MySQL connection.
[Default: localhost]
--port=ARG Use ARG as the port for the MySQL connection.
--password=ARG Use ARG as the password for the MySQL connection.
@@ -273,10 +278,11 @@ Options:
if __name__ == "__main__":
try:
# Parse the command-line options, if any.
dbname = version = hostname = username = password = None
dbname = version = hostname = port = username = password = None
opts, args = getopt.getopt(sys.argv[1:], '', [ 'dbname=',
'help',
'hostname=',
'port=',
'password=',
'username=',
'version=',
@@ -290,6 +296,8 @@ if __name__ == "__main__":
dbname = value
elif name == '--hostname':
hostname = value
elif name == '--port':
port = value
elif name == '--username':
username = value
elif name == '--password':
@@ -302,7 +310,9 @@ if __name__ == "__main__":
# Prompt for information not provided via command-line options.
if hostname is None:
hostname = raw_input("MySQL Hostname [default: localhost]: ") or ""
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:
@@ -310,7 +320,7 @@ if __name__ == "__main__":
if dbname is None:
dbname = raw_input("ViewVC Database Name [default: ViewVC]: ") or "ViewVC"
# Create the database
# Create the database.
dscript = string.replace(DATABASE_SCRIPT_COMMON, "<dbname>", dbname)
if version == "1.0":
print BONSAI_COMPAT
@@ -318,16 +328,22 @@ if __name__ == "__main__":
else:
dscript = dscript + DATABASE_SCRIPT_VERSION_1
host_option = hostname and "--host=%s" % (hostname) or ""
# 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 "\
% (username, password, 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" \
% (username, password, host_option)
cmd = "{ mysql %s ; } 2>&1" % (cmd_args)
pipes = popen2.Popen3(cmd)
pipes.tochild.write(dscript)
pipes.tochild.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

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 2004-2008 The ViewCVS Group. All Rights Reserved.
# 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
@@ -153,7 +153,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)
@@ -164,21 +164,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))
@@ -242,6 +254,7 @@ 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
@@ -252,18 +265,24 @@ def main(command, repository, revs=[], verbose=0, force=0):
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':
@@ -281,7 +300,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
@@ -330,12 +349,6 @@ if __name__ == '__main__':
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':
@@ -358,6 +371,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 []

View File

@@ -86,8 +86,8 @@
## cvs_roots: Specifies each of the CVS roots on your system and
## assigns names to them. Each root should be given by a "name: path"
## value. Multiple roots should be separated by commas and can be
## placed on separate lines.
## value (where the path is an absolute filesystem path). Multiple roots
## should be separated by commas and can be placed on separate lines.
##
## Example:
## cvs_roots = cvsroot: /opt/cvs/repos1,
@@ -97,8 +97,13 @@
## svn_roots: Specifies each of the Subversion roots (repositories) on
## your system and assigns names to them. Each root should be given by
## a "name: path" value. Multiple roots should be separated by commas
## and can be placed on separate lines.
## a "name: path" value (where the path is an absolute filesystem path).
## Multiple roots should be separated by commas and can be placed on
## separate lines.
##
## NOTE: ViewVC offers *experimental* support for displaying remote
## Subversion repositories. Simply use the repository's URL instead
## of a local filesystem path when defining the root.
##
## Example:
## svn_roots = svnrepos: /opt/svn/,
@@ -106,27 +111,48 @@
##
#svn_roots =
## root_parents: Specifies a list of directories in which any number of
## repositories may reside. Rather than force you to add a new entry
## to 'cvs_roots' or 'svn_roots' each time you create a new repository,
## ViewVC rewards you for organising all your repositories under a few
## parent directories by allowing you to simply specifiy just those
## parent directories. ViewVC will then notice each repository in that
## directory as a new root whose name is the subdirectory of the parent
## path in which that repository lives.
## root_parents: Specifies a list of directories under which any
## number of repositories may reside. You can specify multiple root
## parents separated by commas or new lines, each of which is of the
## form "path: type" (where the type is either "cvs" or "svn", and
## the path is an absolute filesystem path).
##
## You can specify multiple parent paths separated by commas or new lines.
## Rather than force you to add a new entry to 'cvs_roots' or
## 'svn_roots' each time you create a new repository, ViewVC rewards
## you for organizing all your repositories under a few parent
## directories by allowing you to simply tell it about those parent
## directories. ViewVC will then notice each repository of the
## specified type in that directory as a root whose name is the
## subdirectory in which that repository lives.
##
## WARNING: these names can, of course, clash with names you have
## defined in your cvs_roots or svn_roots configuration items. If this
## occurs, you can either rename the offending repository on disk, or
## grant new names to the clashing item in cvs_roots or svn_roots.
## Each parent path is processed sequentially, so repositories under
## later parent paths may override earlier ones.
## For example, if you have three Subversion repositories located at
## /opt/svn/projects, /opt/svn/websites, and /opt/svn/devstuff, you
## could list them individually in svn_roots like so:
##
## svn_roots = projects: /opt/svn/projects,
## websites: /opt/svn/websites,
## devstuff: /opt/svn/devstuff
##
## or you could instead use the root_parents configuration option:
##
## root_parents = /opt/svn: svn
##
## The benefit of this latter approach is that, as you add new
## repositories to your /opt/svn directory, they automatically become
## available for display in ViewVC without additional configuration.
##
## WARNING: the root names derived for repositories configured via the
## root_parents option can, of course, clash with names you have
## defined in your cvs_roots or svn_roots configuration items. If
## this occurs, you can either rename the offending repository on
## disk, or grant new names to the clashing item in cvs_roots or
## svn_roots. Each parent path is processed sequentially, so the
## names of repositories under later parent paths may override earlier
## ones.
##
## Example:
## root_parents = /opt/svn : svn,
## /opt/cvs : cvs
## root_parents = /opt/svn: svn,
## /opt/cvs: cvs
##
#root_parents =
@@ -325,10 +351,64 @@
## allowed_views: List the ViewVC views which are enabled. Views not
## in this comma-delited list will not be served (or, will return an
## error on attempted access).
## Possible values: "annotate", "co", "diff", "markup", "roots", "tar"
##
## Valid items for this list include: "annotate", "co", "diff", "markup",
## "roots", "tar".
##
## ----------+---------------------------------------------------------
## VIEW | DESCRIPTION
## ----------+---------------------------------------------------------
## annotate | The 'annotate' view shows the contents of a single
## | revision of a versioned file in exactly the same way as
## | the markup view, but with additional line-by-line
## | change attribution (the revision number, author, etc.
## | the most recent edit to that line of text as of the
## | displayed version).
## ----------+---------------------------------------------------------
## co | The 'co' (aka "checkout" or "download") view isn't
## | really a branded view at all, but allows for direct
## | downloading of the contents of a single revision of a
## | versioned file.
## ----------+---------------------------------------------------------
## diff | The 'diff' view displays line-based differences between
## | two revisions of a versioned file in a variety of
## | different user-selectable formats.
## ----------+---------------------------------------------------------
## markup | The 'markup' view shows the contents of a single
## | revision of a versioned file, with syntax highlighting
## | where possible and enabled. It can also optionally
## | show change log information for that revision of the
## | file.
## ----------+---------------------------------------------------------
## roots | The 'roots' view is a simple listing of the various
## | repositories which ViewVC has been configured to serve
## | to users.
## ----------+---------------------------------------------------------
## tar | The 'tar' view isn't a branded view, but generates
## | a GNU Tar archive file containing a single versioned
## | directory and its contents (recursively).
## ----------+---------------------------------------------------------
##
#allowed_views = annotate, diff, markup, roots
## Comma-delimited list of MIME content types (with support for fnmatch-
## style glob characters) which are considered not-human-readable and for
## which ViewVC will neither generate links to, nor support the direct
## display of, non-checkout views which carry the file's content (the
## 'markup', 'annotate', 'diff', and 'patch' views).
##
## NOTE: Handling of this option is given priority over ViewVC's
## longstanding support for showing web-friendly file formats -- even
## binary ones such as "image/jpeg" and "image/gif" -- in the 'markup'
## view. Thus, if you add "image/*" to this list, 'markup'-view
## display of JPEG, GIF, and PNG images will be disabled.
##
## Example:
## binary_mime_types = application/octet-stream, image/*, application/pdf,
## application/vnd*, application/msword, audio/*
#
#binary_mime_types =
## authorizer: The name of the ViewVC authorizer plugin to use when
## authorizing access to repository contents. This value must be the
## name of a Python module addressable as vcauth.MODULENAME (most
@@ -371,6 +451,26 @@
##
#mangle_email_addresses = 0
## custom_log_formatting: Specifies mappings of regular expressions to
## substitution format strings used to URL-ize strings found in
## revision log messages. Multiple mappings (specified as
## REGEXP:FORMATSTRING) may be defined, separated by commas.
##
## NOTE: Due to a limitation of the configuration format, commas may
## not be used in the regular expression portion of each mapping.
## Commas "in the raw" may not be used in the format string portion,
## either, but you can probably use the URI-encoded form of the comma
## ("%2C") instead with no ill side-effects. If you must specify a
## colon character in either the regular expression or the format
## string, escape it with a preceding backslash ("\:").
##
## Example:
## custom_log_formatting =
## artf[0-9]+ : http://example.com/tracker?id=\0,
## issue ([0-9]+) : http://example.com/bug?id=\1&opts=full%2csortby=id
##
#custom_log_formatting =
## default_file_view: "log", "co", or "markup"
## Controls whether the default view for file URLs is a checkout view or
## a log view. "log" is the default for backwards compatibility with old
@@ -410,6 +510,15 @@
##
#svn_ignore_mimetype = 0
## max_filesize_kbytes: Limit ViewVC's processing of file contents in
## "markup" and "annotate" views to only those files which are smaller
## than this setting, expressed in kilobytes. Set to 0 to disable
## this safeguard.
##
## NOTE: The "co" and "tar" views are unaffected by this setting.
##
#max_filesize_kbytes = 512
## svn_config_dir: Path of the Subversion runtime configuration
## directory ViewVC should consult for various things, including cached
## remote authentication credentials. If unset, Subversion will use
@@ -479,7 +588,7 @@
## (Only works well for C source files, otherwise diff's heuristic falls short.)
## ('-p' option to diff)
##
#hr_funout = 0
#hr_funout = 1
## hr_ignore_white: Ignore whitespace (indendation and stuff) for human
## readable diffs.
@@ -552,6 +661,14 @@
##
#show_subdir_lastmod = 0
## show_roots_lastmod: In the root listing view, show the most recent
## modifications made to the root. (Subversion roots only.)
##
## NOTE: Enabling this feature will significantly reduce the
## performance of the root listing view.
##
#show_roots_lastmod = 0
## show_logs: Show the most recent log entry in directory listings.
##
#show_logs = 1
@@ -569,25 +686,35 @@
##
#use_localtime = 0
## iso8601_dates: Display timestamps using a standard ISO-8601 format.
##
#iso8601_timestamps = 0
## short_log_len: The length (in characters) to which the most recent
## log entry should be truncated when shown in the directory view.
##
#short_log_len = 80
## enable_syntax_coloration: Should we colorize known file content
## syntaxes? [Requires Pygments Python module]
## syntaxes?
##
## NOTE: This feature requires the Pygments Python module
## (http://pygments.org) and works only when ViewVC can determine the
## MIME content type of the file whose contents it wishes to colorize.
## Use the 'mime_types_files' configuration option to specify MIME
## type mapping files useful for making that determination.
##
#enable_syntax_coloration = 1
## tabsize: The number of spaces into which tabstops are converted
## when viewing file contents.
## tabsize: The number of spaces into which horizontal tab characters
## are converted when viewing file contents. Set to 0 to preserve
## tab characters.
##
#tabsize = 8
## detect_encoding: Should we attempt to detect versioned file
## character encodings? [Requires 'chardet' module, and is currently
## used only by the syntax coloration logic -- if enabled -- for the
## 'markup' and 'annotate' views; see 'enable_syntax_coloration'.]
## used only for the 'markup' and 'annotate' views.]
##
#detect_encoding = 0
@@ -629,6 +756,26 @@
##
#log_pagesize = 0
## log_pagesextra: Maximum number of extra pages (based on
## log_pagesize) of revision log data to fetch and present to the user
## as additional options for display. Revision log information
## "beyond" this window is still accessible, but must be navigated to
## in multiple steps.
##
## Example:
## log_pagesize = 100
## log_pagesextra = 3
##
## For a versioned file with 1000 revisions, the above settings would
## present to the user the first 100 of those 1000 revisions, with
## links to three additional pages (the 200-299th revisions, 300-399th
## revisions, and 400-499th revisions) plus a link to the 500th
## revision. Following these links slides the display "window",
## showing the requested set of revisions plus links to three
## additional pages beyond those, and so on.
##
#log_pagesextra = 3
## limit_changes: Maximum number of changed paths shown per commit in
## the Subversion revision view and in query results. This is not a
## hard limit (the UI provides options to show all changed paths), but
@@ -749,6 +896,14 @@
## row_limit: Maximum number of rows returned by a given normal query
## to the database.
##
## NOTE: This limits the amount of data provided to ViewVC by the
## database. It is from this already-reduced data set that ViewVC
## builds the query response it presents to the user, which may or may
## not include still more limiting via the query form's 'limit'
## parameter. In other words, there is no value which the user can use
## in the query form's 'limit' parameter which will cause more data to
## be returned by the database for ViewVC to process.
##
#row_limit = 1000
## rss_row_limit: Maximum number of rows returned by a given query to
@@ -756,6 +911,9 @@
## that RSS readers tend to poll regularly for new data, you might want
## to keep this set to a conservative number.)
##
## See also the `NOTE' for the 'row_limit' option, which applies here
## as well.
##
#rss_row_limit = 100
## check_database_for_root: Check if the repository is found in the
@@ -997,3 +1155,30 @@
#force_username_case =
##---------------------------------------------------------------------------
[query]
## The configuration items in this section are used exclusively by the
## 'query' script, a separate script from ViewVC itself that ships
## with ViewVC and allows for queries into the ViewVC commits
## database. If you aren't using this separate script (which was made
## largely irrelevant by the introduction of an integrated "query"
## view in ViewVC itself, or aren't using the ViewVC commits database
## functionality at all, you can ignore these configurations items
## altogether.
## viewvc_base_url: Base URL at which ViewVC may be accessed on this
## server. The default value for this option is determined at
## run-time by the various front-ends to the query script.
##
## Examples:
## viewvc_base_url = /viewvc.py
## viewvc_base_url = /viewvc.wsgi
## viewvc_base_url = /cgi-bin/viewvc
## viewvc_base_url = viewvc
##
## To disable cross-linking between the query script and ViewVC,
## uncomment this option and leave its value empty.
##
#viewvc_base_url =
##---------------------------------------------------------------------------

View File

@@ -1,6 +1,6 @@
<html>
<head>
<title>ViewVC 1.0 Template Authoring Guide</title>
<title>ViewVC 1.1 Template Authoring Guide</title>
<style>
body {
background-color: rgb(180,193,205);
@@ -38,13 +38,13 @@ td {
</head>
<body>
<h1>ViewVC 1.0 Template Authoring Guide</h1>
<h1>ViewVC 1.1 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>
@@ -802,6 +802,11 @@ td {
<td>String</td>
<td>This is a URL for the markup view of the left file.</td>
</tr>
<tr class="varlevel1">
<td class="varname">patch_href</td>
<td>String</td>
<td>URL of the patch view for the file.</td>
</tr>
<tr class="varlevel1">
<td class="varname">raw_diff</td>
<td>String</td>
@@ -1821,6 +1826,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>
@@ -2144,6 +2157,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>
@@ -2157,17 +2202,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

@@ -1192,13 +1192,6 @@ th.caption {
commands to back out changes instead showing a normal query result
page</td>
</tr>
<tr>
<td><code>limit=<var>LIMIT</var></code></td>
<td>optional</td>
<td>maximum number of file-revisions to process during a
query. Default is value of <code>row_limit</code> configuration
option</td>
</tr>
<tr>
<td><code>limit_changes=<var>LIMIT_CHANGES</var></code></td>
<td>optional</td>

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2009 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-2010 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
@@ -74,7 +74,8 @@ 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)

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-2010 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
@@ -88,6 +88,11 @@ import fnmatch
# | vhosts |
# | |
# `-----------'
# ,-----------.
# | |
# | query |
# | |
# `-----------'
#
# ### TODO: Figure out what this all means for the 'kv' stuff.
#
@@ -100,12 +105,15 @@ class Config:
'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',
@@ -145,11 +153,11 @@ class Config:
self.conf_path = os.path.isfile(pathname) and pathname or None
self.base = os.path.dirname(pathname)
self.parser = ConfigParser.ConfigParser()
self.parser.optionxform = lambda x: x # don't case-normalize option names.
self.parser.read(self.conf_path or [])
for section in self.parser.sections():
if self._is_allowed_section(self.parser, section,
self._base_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'):
@@ -172,6 +180,7 @@ class Config:
fname = string.replace(fname, '%lang%', language)
parser = ConfigParser.ConfigParser()
parser.optionxform = lambda x: x # don't case-normalize option names.
parser.read(os.path.join(self.base, fname))
for section in parser.sections():
for option in parser.options(section):
@@ -214,7 +223,7 @@ class Config:
setattr(sc, opt, value)
def _is_allowed_section(self, parser, section, allowed_sections):
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."""
@@ -227,7 +236,7 @@ class Config:
return 1
return 0
def _is_allowed_override(self, parser, sectype, secspec, section):
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
@@ -240,7 +249,7 @@ class Config:
if section[:lcv] != cv:
return None
base_section = section[lcv:]
if self._is_allowed_section(parser, base_section,
if self._is_allowed_section(base_section,
self._allowed_overrides[sectype]):
return base_section
raise IllegalOverrideSection(sectype, section)
@@ -253,8 +262,7 @@ class Config:
# Overlay any option sections associated with this vhost name.
for section in parser.sections():
base_section = self._is_allowed_override(parser, 'vhost',
canon_vhost, section)
base_section = self._is_allowed_override('vhost', canon_vhost, section)
if base_section:
self._process_section(parser, section, base_section)
@@ -280,8 +288,7 @@ class Config:
return
for section in self.parser.sections():
base_section = self._is_allowed_override(self.parser, 'root',
rootname, section)
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
@@ -322,7 +329,7 @@ class Config:
assert(self.root_options_overlayed == 0)
if not self.conf_path:
return None
return None, {}
# Figure out the authorizer by searching first for a per-root
# override, then falling back to the base/vhost configuration.
@@ -393,11 +400,14 @@ class Config:
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
@@ -415,10 +425,12 @@ class Config:
self.options.template_dir = "templates"
self.options.docroot = None
self.options.show_subdir_lastmod = 0
self.options.show_roots_lastmod = 0
self.options.show_logs = 1
self.options.show_log_in_markup = 1
self.options.cross_copies = 1
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
@@ -428,6 +440,7 @@ class Config:
self.options.use_re_search = 0
self.options.dir_pagesize = 0
self.options.log_pagesize = 0
self.options.log_pagesextra = 3
self.options.limit_changes = 100
self.templates.diff = None
@@ -453,6 +466,8 @@ 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2009 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
@@ -38,13 +38,12 @@ 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
@@ -169,6 +168,9 @@ 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()
@@ -309,8 +311,7 @@ class CheckinDatabase:
minus_count = commit.GetMinusCount() or '0'
description_id = self.GetDescriptionID(commit.GetDescription())
commits_table = self._version >= 1 and 'commits' or 'checkins'
sql = "REPLACE INTO %s" % (commits_table)
sql = "REPLACE INTO %s" % (self.GetCommitsTable())
sql = sql + \
" (type,ci_when,whoid,repositoryid,dirid,fileid,revision,"\
" stickytag,branchid,addedlines,removedlines,descid)"\
@@ -351,9 +352,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":
@@ -363,8 +372,8 @@ class CheckinDatabase:
return "(%s)" % (string.join(sqlList, " OR "))
def CreateSQLQueryString(self, query):
commits_table = self._version >= 1 and 'commits' or 'checkins'
def CreateSQLQueryString(self, query, detect_leftover=0):
commits_table = self.GetCommitsTable()
tableList = [(commits_table, None)]
condList = []
@@ -444,13 +453,14 @@ class CheckinDatabase:
conditions = string.join(joinConds + condList, " AND ")
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 %s.* FROM %s %s %s %s" \
% (commits_table, tables, conditions, order_by, limit)
@@ -458,14 +468,20 @@ class CheckinDatabase:
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,
@@ -504,13 +520,15 @@ class CheckinDatabase:
if file_id == None:
return None
commits_table = self._version >= 1 and 'commits' or 'checkins'
sql = "SELECT * FROM %s WHERE "\
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"\
% (commits_table)
% (self.GetCommitsTable())
sql_args = (repository_id, dir_id, file_id, commit.GetRevision())
cursor = self.db.cursor()
@@ -527,10 +545,9 @@ class CheckinDatabase:
def sql_delete(self, table, key, value, keep_fkey = None):
sql = "DELETE FROM %s WHERE %s=%%s" % (table, key)
sql_args = (value, )
commits_table = self._version >= 1 and 'commits' or 'checkins'
if keep_fkey:
sql += " AND %s NOT IN (SELECT %s FROM %s WHERE %s = %%s)" \
% (key, keep_fkey, commits_table, keep_fkey)
% (key, keep_fkey, self.GetCommitsTable(), keep_fkey)
sql_args = (value, value)
cursor = self.db.cursor()
cursor.execute(sql, sql_args)
@@ -556,7 +573,10 @@ class CheckinDatabase:
self.sql_purge('descs', 'id', 'descid', 'commits')
self.sql_purge('people', 'id', 'whoid', 'commits')
else:
sql = "SELECT * FROM checkins WHERE repositoryid=%s"
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)
@@ -769,8 +789,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
@@ -790,7 +811,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 = []
@@ -798,6 +820,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))
@@ -843,6 +868,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
@@ -861,7 +900,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

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

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])
@@ -465,13 +465,13 @@ class Template:
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)
@@ -637,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
@@ -653,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:
@@ -666,14 +675,16 @@ 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 TemplateData:
@@ -715,7 +726,7 @@ 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)
@@ -828,20 +839,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-2010 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-2009 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-2010 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
@@ -217,8 +217,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):
@@ -312,11 +313,7 @@ def is_forbidden(cfg, cvsroot_name, module):
def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
ob = _item(num_files=len(files), files=[])
if desc:
ob.log = string.replace(server.escape(desc), '\n', '<br />')
else:
ob.log = '&nbsp;'
ob.log = desc and string.replace(server.escape(desc), '\n', '<br />') or ''
for commit in files:
repository = commit.GetRepository()
@@ -350,9 +347,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)
@@ -380,12 +378,15 @@ 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 = [ ]
@@ -396,8 +397,8 @@ def run_query(server, cfg, form_data, viewvc_link):
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)
@@ -420,7 +421,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:
@@ -429,12 +430,18 @@ 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'
docroot = cfg.options.docroot
if docroot is None and viewvc_link:
docroot = viewvc_link + '/' + viewvc.docroot_magic_path
data = ezt.TemplateData({
'cfg' : cfg,
'address' : cfg.general.address,
@@ -444,14 +451,11 @@ def main(server, cfg, viewvc_link):
'directory' : server.escape(form_data.directory),
'file' : server.escape(form_data.file),
'who' : server.escape(form_data.who),
'docroot' : cfg.options.docroot is None \
and viewvc_link + '/' + viewvc.docroot_magic_path \
or cfg.options.docroot,
'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,

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 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,8 +23,8 @@ 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
@@ -33,6 +33,7 @@ server = None
# that character as-is, and sometimes needs to embed escaped values
# into HTML attributes.
def escape(s):
s = str(s)
s = string.replace(s, '&', '&amp;')
s = string.replace(s, '>', '&gt;')
s = string.replace(s, '<', '&lt;')
@@ -185,7 +186,7 @@ class CgiServer(Server):
def FieldStorage(fp=None, headers=None, outerboundary="",
environ=os.environ, keep_blank_values=0, strict_parsing=0):
return cgi.FieldStorage(fp, headers, outerboundary, environ,
keep_blank_values, strict_parsing)
keep_blank_values, strict_parsing)
def write(self, s):
sys.stdout.write(s)
@@ -197,6 +198,63 @@ 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
global cgi
import cgi
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)

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
@@ -29,7 +29,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 +52,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
@@ -23,7 +23,14 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
def check_root_access(self, rootname):
return 1
def check_universal_access(self, rootname):
# If there aren't any forbidden paths, we can grant universal read
# access. Otherwise, we make no claim.
if not self.forbidden:
return 1
return None
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
# No path? No problem.
if not path_parts:

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
@@ -46,6 +46,13 @@ 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:

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
@@ -34,9 +34,9 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
# See if the admin wants us to do case normalization of usernames.
self.force_username_case = params.get('force_username_case')
if self.force_username_case == "upper":
self.username = username.upper()
self.username = username and string.upper(username) or username
elif self.force_username_case == "lower":
self.username = username.lower()
self.username = username and string.lower(username) or username
elif not self.force_username_case:
self.username = username
else:
@@ -54,7 +54,10 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
# option names.
cp = ConfigParser()
cp.optionxform = lambda x: x
cp.read(self.authz_file)
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 = []
@@ -221,6 +224,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

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
@@ -76,7 +76,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 +86,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 +170,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 +207,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:

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,6 +14,7 @@ import os.path
def canonicalize_rootpath(rootpath):
assert os.path.isabs(rootpath)
return os.path.normpath(rootpath)
@@ -22,6 +23,7 @@ def expand_root_parent(parent_path):
# "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"))
@@ -33,6 +35,23 @@ 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."""
assert os.path.isabs(parent_path)
# 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.
rootpath = os.path.join(parent_path, rootname)
if os.path.exists(os.path.join(parent_path, "CVSROOT", "config")):
return canonicalize_rootpath(rootpath)
if os.path.exists(os.path.join(rootpath, "CVSROOT", "config")):
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
@@ -40,6 +40,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
@@ -144,6 +149,11 @@ class BaseCVSRepository(vclib.Repository):
% (string.join(path_parts, "/")))
rcsfile = self.rcsfile(path_parts, 1)
return os.access(rcsfile, os.X_OK)
def filesize(self, path_parts, rev):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
return -1
class BinCVSRepository(BaseCVSRepository):
@@ -162,7 +172,14 @@ 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, "/")))
@@ -170,12 +187,14 @@ class BinCVSRepository(BaseCVSRepository):
rev_flag = '-p'
else:
rev_flag = '-p' + rev
if options.get('cvs_oldkeywords', 0):
kv_flag = '-ko'
else:
kv_flag = '-kkv'
full_name = self.rcsfile(path_parts, root=1, v=0)
used_rlog = 0
tip_rev = None # used only if we have to fallback to using rlog
fp = self.rcs_popen('co', (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:
@@ -318,13 +337,13 @@ 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, "/")))
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):
@@ -1022,16 +1041,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-2008 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
@@ -414,7 +414,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 +428,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 +448,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
@@ -127,9 +127,9 @@ class CCVSRepository(BaseCVSRepository):
% (string.join(path_parts2, "/")))
temp1 = tempfile.mktemp()
open(temp1, 'wb').write(self.openfile(path_parts1, rev1)[0].getvalue())
open(temp1, 'wb').write(self.openfile(path_parts1, rev1, {})[0].getvalue())
temp2 = tempfile.mktemp()
open(temp2, 'wb').write(self.openfile(path_parts2, rev2)[0].getvalue())
open(temp2, 'wb').write(self.openfile(path_parts2, rev2, {})[0].getvalue())
r1 = self.itemlog(path_parts1, rev1, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
r2 = self.itemlog(path_parts2, rev2, vclib.SORTBY_DEFAULT, 0, 0, {})[-1]
@@ -142,17 +142,17 @@ 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)
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, "/")))
@@ -200,6 +200,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 +214,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 +232,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

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-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
@@ -194,7 +194,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)

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

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-2009 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,10 +18,12 @@ import os
import string
import re
import tempfile
import popen2
import time
import urllib
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev, _fix_subversion_exception
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 +54,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 +127,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))
@@ -88,13 +156,17 @@ class LogCollector:
if change.copyfrom_path:
this_path = change.copyfrom_path + self.path[len(changed_path):]
if self.show_all_logs or this_path:
entry = Revision(revision, _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 +227,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 +236,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 +274,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 +298,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,11 +311,13 @@ 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)
# 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
@@ -267,29 +331,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 +395,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 +421,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,12 +456,12 @@ 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)
@@ -371,6 +475,15 @@ 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, '/')
@@ -378,8 +491,11 @@ class RemoteSubversionRepository(vclib.Repository):
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)
@@ -388,58 +504,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
@@ -452,31 +649,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 --##
@@ -495,23 +722,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
@@ -546,3 +766,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-2010 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
@@ -22,6 +22,7 @@ import time
import tempfile
import popen
import re
import urllib
from svn import fs, repos, core, client, delta
@@ -42,7 +43,14 @@ def _fix_subversion_exception(e):
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
@@ -108,11 +116,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 = 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):
@@ -183,59 +217,6 @@ class NodeHistory:
def __getitem__(self, idx):
return self.histories[idx]
def _get_history(svnrepos, path, rev, path_type, limit=0, options={}):
if svnrepos.youngest == 0:
return []
rev_paths = []
fsroot = svnrepos._getroot(rev)
show_all_logs = options.get('svn_show_all_dir_logs', 0)
if not show_all_logs:
# See if the path is a file or directory.
kind = fs.check_path(fsroot, path)
if kind is core.svn_node_file:
show_all_logs = 1
# Instantiate a NodeHistory collector object, and use it to collect
# history items for PATH@REV.
history = NodeHistory(svnrepos.fs_ptr, show_all_logs, limit)
try:
repos.svn_repos_history(svnrepos.fs_ptr, path, history.add_history,
1, rev, options.get('svn_cross_copies', 0))
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err != _SVN_ERR_CEASE_INVOCATION:
raise
# Now, iterate over those history items, checking for changes of
# location, pruning as necessitated by authz rules.
for hist_rev, hist_path in history:
path_parts = _path_parts(hist_path)
if not vclib.check_path_access(svnrepos, path_parts, path_type, hist_rev):
break
rev_paths.append([hist_rev, hist_path])
return rev_paths
def _log_helper(svnrepos, path, rev, lockinfo):
rev_root = fs.revision_root(svnrepos.fs_ptr, rev)
# Was this path@rev the target of a copy?
copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path)
# Assemble our LogEntry
date, author, msg, changes = svnrepos._revinfo(rev)
if fs.is_file(rev_root, path):
size = fs.file_length(rev_root, path)
else:
size = None
entry = Revision(rev, date, author, msg, size, lockinfo, path,
copyfrom_path and _cleanup_path(copyfrom_path),
copyfrom_rev)
return entry
def _get_last_history_rev(fsroot, path):
history = fs.node_history(fsroot, path)
history = fs.history_prev(history, 0)
@@ -314,12 +295,13 @@ class FileContentsPipe:
class BlameSource:
def __init__(self, local_url, rev, first_rev, config_dir):
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()
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([])
@@ -338,6 +320,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))
@@ -406,6 +390,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
@@ -421,19 +409,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)
@@ -472,7 +455,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
@@ -521,20 +504,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:
@@ -555,43 +537,70 @@ 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, self.config_dir)
source = BlameSource(_rootpath2url(self.rootpath, path), youngest_rev,
oldest_rev, include_text, self.config_dir)
return source, youngest_rev
def revinfo(self, rev):
return self._revinfo(rev, 1)
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
p1 = self._getpath(path_parts1)
p2 = self._getpath(path_parts2)
r1 = self._getrev(rev1)
r2 = self._getrev(rev2)
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
raise vclib.ItemNotFound(path_parts1)
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
raise vclib.ItemNotFound(path_parts2)
args = vclib._diff_args(type, options)
def _date_from_rev(rev):
date, author, msg, revprops, changes = self._revinfo(rev)
return date
try:
temp1 = temp_checkout(self, p1, r1)
temp2 = temp_checkout(self, p2, r2)
info1 = p1, _date_from_rev(r1), r1
info2 = p2, _date_from_rev(r2), r2
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
except core.SubversionException, e:
_fix_subversion_exception(e)
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.InvalidRevision
raise
def isexecutable(self, path_parts, rev):
props = self.itemprops(path_parts, rev) # does authz-check
return props.has_key(core.SVN_PROP_EXECUTABLE)
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 _revinfo_helper(rev, include_changed_paths):
# Get the revision property info. (Would use
# editor.get_root_props(), but something is broken there...)
revprops = fs.revision_proplist(self.fs_ptr, rev)
msg = revprops.get(core.SVN_PROP_REVISION_LOG)
author = revprops.get(core.SVN_PROP_REVISION_AUTHOR)
datestr = revprops.get(core.SVN_PROP_REVISION_DATE)
date = _datestr_to_date(datestr)
# Optimization: If our caller doesn't care about the changed
# paths, and we don't need them to do authz determinations, let's
# get outta here.
if self.auth is None and not include_changed_paths:
return date, author, msg, None
# If we get here, then we either need the changed paths because we
# were asked for them, or we need them to do authorization checks.
# Either way, we need 'em, so let's get 'em.
fsroot = self._getroot(rev)
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)
@@ -644,10 +653,12 @@ class LocalSubversionRepository(vclib.Repository):
if vclib.check_path_access(self, parts, pathtype, rev):
if is_copy and change.base_path and (change.base_path != path):
parts = _path_parts(change.base_path)
if not vclib.check_path_access(self, parts, pathtype, change.base_rev):
if not vclib.check_path_access(self, parts, pathtype,
change.base_rev):
is_copy = 0
change.base_path = None
change.base_rev = None
found_unreadable = 1
changedpaths[path] = SVNChangedPath(path, rev, pathtype,
change.base_path,
change.base_rev, action,
@@ -656,24 +667,116 @@ class LocalSubversionRepository(vclib.Repository):
found_readable = 1
else:
found_unreadable = 1
return found_readable, found_unreadable, changedpaths.values()
def _get_change_copyinfo(fsroot, path, change):
# If 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)
# If our caller doesn't care about changed paths, we must be
# here for authz reasons only. That means the minute we've
# found both a readable and an unreadable path, we can bail out.
if (not include_changed_paths) and found_readable and found_unreadable:
return date, author, None, None
# 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)
# Okay, we've process all our paths. Let's filter our metadata,
# and return the requested data.
# Filter our metadata where necessary, and return the requested data.
if found_unreadable:
msg = None
if not found_readable:
author = None
date = None
if include_changed_paths:
return date, author, msg, changedpaths.values()
else:
return date, author, msg, 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
@@ -681,45 +784,55 @@ class LocalSubversionRepository(vclib.Repository):
rev = self._getrev(rev)
cached_info = self._revinfo_cache.get(rev)
if not cached_info \
or (include_changed_paths and cached_info[3] is None):
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 cached_info[0], cached_info[1], cached_info[2], cached_info[3]
return tuple(cached_info)
def revinfo(self, rev):
return self._revinfo(rev, 1)
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
p1 = self._getpath(path_parts1)
p2 = self._getpath(path_parts2)
r1 = self._getrev(rev1)
r2 = self._getrev(rev2)
if not vclib.check_path_access(self, path_parts1, vclib.FILE, rev1):
raise vclib.ItemNotFound(path_parts1)
if not vclib.check_path_access(self, path_parts2, vclib.FILE, rev2):
raise vclib.ItemNotFound(path_parts2)
args = vclib._diff_args(type, options)
def _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 _date_from_rev(rev):
date, author, msg, changes = self._revinfo(rev)
return date
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:
temp1 = temp_checkout(self, p1, r1)
temp2 = temp_checkout(self, p2, r2)
info1 = p1, _date_from_rev(r1), r1
info2 = p2, _date_from_rev(r2), r2
return vclib._diff_fp(temp1, temp2, info1, info2, self.diff_cmd, args)
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 == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.InvalidRevision
raise
if e.apr_err != _SVN_ERR_CEASE_INVOCATION:
raise
def isexecutable(self, path_parts, rev):
props = self.itemprops(path_parts, rev) # does authz-check
return props.has_key(core.SVN_PROP_EXECUTABLE)
# 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, '/')
@@ -728,8 +841,11 @@ class LocalSubversionRepository(vclib.Repository):
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)
@@ -742,7 +858,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
@@ -820,3 +949,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

View File

@@ -27,7 +27,9 @@ numbers, and not literal):
3. Verify that copyright years are correct in both the license-1.html
file and the source code.
4. Update and commit the 'CHANGES' file.
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 +40,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).
*** Do NOT commit this change. ***
10. Tag the release:
svn cp -m "Tag the X.Y.Z final release." . ^/tags/X.Y.Z
This will create a copy of the release branch, plus your local
modifications to the svn:externals property and lib/viewvc.py
file, to the tag location.
11. Revert the changes in your working copy.
svn revert -R .
12. Go into an empty directory and run the 'make-release' script:
tools/make-release viewvc-X.Y.Z tags/X.Y.Z
10. Verify the archive files:
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.

View File

@@ -94,9 +94,6 @@ form { margin: 0; }
.vc_properties {
margin: 1em 0;
}
.vc_properties h2 {
font-size: 115%;
}
/*** File Content Markup Styles ***/
@@ -272,3 +269,14 @@ table.vc_idiff tbody th {
.vc_query_form {
background-color: #e6e6e6;
}
/*** Warning! ***/
.vc_warning {
border-width: 1px 2px 2px 2px;
border-color: black;
border-style: solid;
background-color: red;
color: white;
padding: 0.5em;
}

View File

@@ -9,7 +9,11 @@
[# ------------------------------------------------------------------------- ]
[# setup page definitions]
[define page_title]Contents of /[where][end]
[is annotation "annotated"]
[define page_title]Annotation of /[where][end]
[else]
[define page_title]Contents of /[where][end]
[end]
[define help_href][docroot]/help_rootview.html[end]
[# end]

View File

@@ -5,7 +5,7 @@
<table>
<tr>
<td>[if-any cfg.general.address]<address><a href="mailto:[cfg.general.address]">[cfg.general.address]</a></address>[else]&nbsp;[end]</td>
<td style="text-align: right;"><strong><a href="[help_href]">ViewVC Help</a></strong></td>
<td style="text-align: right;">[if-any help_href]<strong><a href="[help_href]">ViewVC Help</a></strong>[else]&nbsp;[end]</td>
</tr>
<tr>
<td>Powered by <a href="http://viewvc.tigris.org/">ViewVC [vsn]</a></td>

View File

@@ -5,7 +5,7 @@
<head>
<title>[if-any rootname][[][rootname]][else]ViewVC[end] [page_title]</title>
<meta name="generator" content="ViewVC [vsn]" />
<link rel="shortcut icon" href="[docroot]/images/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="[docroot]/images/favicon.ico" />
<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />
[if-any rss_href]<link rel="alternate" type="application/rss+xml" title="RSS [[][rootname]][where]" href="[rss_href]" />[end]
</head>

View File

@@ -4,13 +4,13 @@
<!-- ViewVC :: http://www.viewvc.org/ -->
<head>
<title>Checkin Database Query</title>
<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />
[if-any docroot]<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />[end]
</head>
<body>
[# setup page definitions]
[define help_href][docroot]/help_query.html[end]
[define help_href][if-any docroot][docroot]/help_query.html[end][end]
[# end]
<p>
@@ -158,7 +158,15 @@
[is query "skipped"]
[else]
<p><strong>[num_commits]</strong> matches found.</p>
[if-any row_limit_reached]
<p class="vc_warning">WARNING: These query results have been
artificially limited by an administrative threshold value and do
<em>not</em> represent the entirety of the data set which matches
the query. Consider modifying your query to be more specific</a>,
using your version control tool's query capabilities, or asking
your administrator to raise the database response size
threshold.</p>
[end]
[if-any commits]
<table cellspacing="0" cellpadding="2">
<thead>
@@ -202,21 +210,21 @@
</td>
[# uncommment, if you want a separate Description column:
{if-index commits.files first{
{if-index commits.files first}
<td style="vertical-align:top;" rowspan="{commits.num_files}">
{commits.log}
{if-any commits.log}{commits.log}{else}&nbsp;{end}
</td>
{end}
(substitute brackets for the braces)
]
</tr>
[# and also take the following out in the "Description column"-case:]
[# and also take the following out in the "Description column" case:]
[if-index commits.files last]
<tr class="vc_row_[if-index commits even]even[else]odd[end]">
<td>&nbsp;</td>
<td colspan="5"><strong>Log:</strong><br />
<pre class="vc_log">[commits.log]</pre></td>
<pre class="vc_log">[if-any commits.log][commits.log][else]&nbsp;[end]</pre></td>
</tr>
[end]
[# ---]

View File

@@ -7,6 +7,15 @@
<p><strong>[english_query]</strong></p>
[# <!-- {sql} --> ]
[if-any row_limit_reached]
<p class="vc_warning">WARNING: These query results have been
artificially limited by an administrative threshold value and do
<em>not</em> represent the entirety of the data set which matches
the query. Consider <a href="[queryform_href]">modifying your
query to be more specific</a>, using your version control tool's
query capabilities, or asking your administrator to raise the
database response size threshold.</p>
[end]
<p><a href="[queryform_href]">Modify query</a></p>
<p><a href="[backout_href]">Show commands which could be used to back out these changes</a></p>

View File

@@ -6,6 +6,7 @@
[include "include/header.ezt" "revision"]
<hr />
<form method="get" action="[jump_rev_action]">
<table cellspacing="1" cellpadding="2" style="width: auto;">
<tr align="left">
@@ -42,7 +43,7 @@
<hr />
<p><strong>Changed paths:</strong></p>
<h2>Changed paths</h2>
<table cellspacing="1" cellpadding="2">
<thead>
@@ -77,4 +78,5 @@
</tbody>
</table>
[include "include/props.ezt"]
[include "include/footer.ezt"]

View File

@@ -9,6 +9,12 @@
<thead>
<tr>
<th class="vc_header_sort">Name</th>
[is cfg.options.show_roots_lastmod "1"]
<th class="vc_header">Revision</th>
<th class="vc_header">Age</th>
<th class="vc_header">Author</th>
<th class="vc_header">Log</th>
[end]
</tr>
</thead>
@@ -20,6 +26,12 @@
<img src="[docroot]/images/dir.png" alt="" class="vc_icon" />
[roots.name]</a>
</td>
[is cfg.options.show_roots_lastmod "1"]
<td style="width:20">&nbsp;[if-any roots.log_href]<a href="[roots.log_href]">[roots.rev]</a>[else][roots.rev][end]</td>
<td style="width:20">&nbsp;[roots.ago]</td>
<td style="width:20">&nbsp;[roots.author]</td>
<td style="width:20">&nbsp;[roots.short_log]</td>
[end]
</tr>
[end]
</tbody>

View File

@@ -7,11 +7,11 @@
<description>[is roottype "svn"]Subversion[else]CVS[end] commits to the[if-any where] [where] directory of the[end] [rootname] repository</description>
[for commits]<item>
<title>[if-any commits.rev][commits.rev]: [end][[commits.author]] [commits.short_log]</title>
<title>[if-any commits.rev][commits.rev]: [end][[commits.author]] [format "xml"][commits.short_log][end]</title>
[if-any commits.rss_url]<link>[commits.rss_url]</link>[end]
<author>[commits.author]</author>
<pubDate>[if-any commits.rss_date][commits.rss_date][else](unknown date)[end]</pubDate>
<description>&lt;pre&gt;[format "xml"][commits.log][end]&lt;/pre&gt;</description>
<description>&#x3C;pre&#x3E;[format "xml"][format "html"][commits.log][end][end]&#x3C;/pre&#x3E;</description>
</item>[end]
</channel>
</rss>

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
# 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-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,7 +1,7 @@
#!/usr/bin/env python
# -*- Mode: python -*-
#
# Copyright (C) 1999-2009 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
@@ -49,6 +49,10 @@ CLEAN_MODE = None
FILE_INFO_LIST = [
("bin/cgi/viewvc.cgi", "bin/cgi/viewvc.cgi", 0755, 1, 0, 0),
("bin/cgi/query.cgi", "bin/cgi/query.cgi", 0755, 1, 0, 0),
("bin/wsgi/viewvc.wsgi", "bin/wsgi/viewvc.wsgi", 0755, 1, 0, 0),
("bin/wsgi/viewvc.fcgi", "bin/wsgi/viewvc.fcgi", 0755, 1, 0, 0),
("bin/wsgi/query.wsgi", "bin/wsgi/query.wsgi", 0755, 1, 0, 0),
("bin/wsgi/query.fcgi", "bin/wsgi/query.fcgi", 0755, 1, 0, 0),
("bin/mod_python/viewvc.py", "bin/mod_python/viewvc.py", 0755, 1, 0, 0),
("bin/mod_python/query.py", "bin/mod_python/query.py", 0755, 1, 0, 0),
("bin/mod_python/handler.py", "bin/mod_python/handler.py", 0755, 1, 0, 0),
@@ -205,6 +209,8 @@ def install_file(src_path, dst_path, mode, subst_path_vars,
temp = raw_input("Do you want to [O]verwrite, [D]o "
"not overwrite, or [V]iew "
"differences? ")
if len(temp) == 0:
continue
temp = string.lower(temp[0])
if temp == "v" and ext not in BINARY_FILE_EXTS:
print """