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

Compare commits

...

57 Commits

Author SHA1 Message Date
cmpilato
3af8715ce8 Tag the 1.1.12 final release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.12@2665 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-03 14:32:34 +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
31 changed files with 1016 additions and 436 deletions

37
CHANGES
View File

@@ -1,3 +1,40 @@
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
* handle file:/// Subversion rootpaths as local roots (issue #446)
* 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)

59
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,8 +231,8 @@ 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
to be effective, "AllowOverride All" or "AllowOverride Options FileInfo"
@@ -227,6 +248,10 @@ 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
@@ -278,13 +303,6 @@ APACHE CONFIGURATION
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
ScriptAlias /query /usr/local/viewvc/bin/wsgi/query.fcgi
3) Restart Apache.
The commands to do this vary. "httpd -k restart" and "apache -k
restart" are two common variants. On RedHat Linux it is done using
the command "/sbin/service httpd restart" and on SuSE Linux it is
done with "rcapache restart". Other systems use "apachectl restart".
4) [Optional] Add access control.
In your httpd.conf you can control access to certain modules by
@@ -306,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-2011 The ViewCVS Group. All rights
reserved.</strong></p>
<p>By using ViewVC, you agree to the terms and conditions set forth
@@ -61,6 +61,7 @@
<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>
</ul>
</body>

View File

@@ -76,8 +76,6 @@ except ImportError:
class Options:
port = 49152 # default TCP/IP port used for the server
start_gui = 0 # No GUI unless requested.
daemon = 0 # stay in the foreground by default
repositories = {} # use default repositories specified in config
host = sys.platform == 'mac' and '127.0.0.1' or 'localhost'
script_alias = 'viewvc'
@@ -707,13 +705,66 @@ def gui(host, port):
# --- command-line interface: ----------------------------------------------
def cli(argv):
def usage():
clean_options = Options()
cmd = os.path.basename(sys.argv[0])
port = clean_options.port
host = clean_options.host
script_alias = clean_options.script_alias
sys.stderr.write("""Usage: %(cmd)s [OPTIONS]
Run a simple, standalone HTTP server configured to serve up ViewVC requests.
Options:
--config-file=FILE (-c) Read configuration options from FILE. If not
specified, ViewVC will look for a configuration
file in its installation tree, falling back to
built-in default values. (Not valid in GUI mode.)
--daemon (-d) Background the server process.
--gui (-g) Pop up a graphical configuration interface.
Requires a valid X11 display connection.
--help Show this usage message and exit.
--host=HOSTNAME (-h) Listen on HOSTNAME. Required for access from a
remote machine. [default: %(host)s]
--htpasswd-file=FILE Authenticate incoming requests, validating against
against FILE, which is an Apache HTTP Server
htpasswd file. (CRYPT only; no DIGEST support.)
--port=PORT (-p) Listen on PORT. [default: %(port)d]
--repository=PATH (-r) Serve the Subversion or CVS repository located
at PATH. This option may be used more than once.
--script-alias=PATH (-s) Use PATH as the virtual script location (similar
to Apache HTTP Server's ScriptAlias directive).
For example, "--script-alias=repo/view" will serve
ViewVC at "http://HOSTNAME:PORT/repo/view".
[default: %(script_alias)s]
""" % locals())
sys.exit(0)
def badusage(errstr):
cmd = os.path.basename(sys.argv[0])
sys.stderr.write("ERROR: %s\n\n"
"Try '%s --help' for detailed usage information.\n"
% (errstr, cmd))
sys.exit(1)
def main(argv):
"""Command-line interface (looks at argv to decide what to do)."""
import getopt
class BadUsage(Exception): pass
short_opts = string.join(['c',
'd:',
short_opts = string.join(['c:',
'd',
'g',
'h:',
'p:',
@@ -723,6 +774,7 @@ def cli(argv):
long_opts = ['daemon',
'config-file=',
'gui',
'help',
'host=',
'htpasswd-file=',
'port=',
@@ -730,113 +782,98 @@ def cli(argv):
'script-alias=',
]
try:
opt_daemon = 0
opt_gui = 0
opt_host = None
opt_port = None
opt_htpasswd_file = None
opt_config_file = None
opt_script_alias = None
opt_repositories = []
# Parse command-line options.
try:
opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
for opt, val in opts:
if opt in ('-g', '--gui'):
options.start_gui = 1
elif opt in ('-r', '--repository'):
if options.repositories: # option may be used more than once:
num = len(options.repositories.keys())+1
symbolic_name = "Repository"+str(num)
options.repositories[symbolic_name] = val
else:
options.repositories["Development"] = val
elif opt in ('-d', '--daemon'):
options.daemon = 1
elif opt in ('-p', '--port'):
try:
options.port = int(val)
except ValueError:
raise BadUsage, "Port '%s' is not a valid port number" % (val)
elif opt in ('-h', '--host'):
options.host = val
elif opt in ('-s', '--script-alias'):
options.script_alias = \
string.join(filter(None, string.split(val, '/')), '/')
elif opt in ('-c', '--config-file'):
if not os.path.isfile(val):
raise BadUsage, "'%s' does not appear to be a valid " \
"configuration file." % (val)
options.config_file = val
elif opt in ('-c', '--htpasswd-file'):
if not os.path.isfile(val):
raise BadUsage, "'%s' does not appear to be a valid " \
"htpasswd file." % (val)
if not has_crypt:
raise BadUsage, "Unable to locate suitable `crypt' module for use " \
"with --htpasswd-file option. If your Python " \
"distribution does not include this module (as is " \
"the case on many non-Unix platforms), consider " \
"installing the `fcrypt' module instead (see " \
"http://carey.geek.nz/code/python-fcrypt/)."
options.htpasswd_file = val
if options.start_gui and options.config_file:
raise BadUsage, "--config-file option is not valid in GUI mode."
if not options.start_gui and not options.port:
raise BadUsage, "You must supply a valid port, or run in GUI mode."
if options.daemon:
pid = os.fork()
if pid != 0:
sys.exit()
if options.start_gui:
gui(options.host, options.port)
return
elif options.port:
def ready(server):
print 'server ready at %s%s' % (server.url, options.script_alias)
serve(options.host, options.port, ready)
return
except (getopt.error, BadUsage), err:
cmd = os.path.basename(sys.argv[0])
port = options.port
host = options.host
script_alias = options.script_alias
if str(err):
sys.stderr.write("ERROR: %s\n\n" % (str(err)))
sys.stderr.write("""Usage: %(cmd)s [OPTIONS]
if opt in ['--help']:
usage()
elif opt in ['-g', '--gui']:
opt_gui = 1
elif opt in ['-r', '--repository']: # may be used more than once
opt_repositories.append(val)
elif opt in ['-d', '--daemon']:
opt_daemon = 1
elif opt in ['-p', '--port']:
opt_port = val
elif opt in ['-h', '--host']:
opt_host = val
elif opt in ['-s', '--script-alias']:
opt_script_alias = val
elif opt in ['-c', '--config-file']:
opt_config_file = val
elif opt in ['--htpasswd-file']:
opt_htpasswd_file = val
except getopt.error, err:
badusage(str(err))
Run a simple, standalone HTTP server configured to serve up ViewVC
requests.
# Validate options that need validating.
class BadUsage(Exception): pass
try:
if opt_port is not None:
try:
options.port = int(opt_port)
except ValueError:
raise BadUsage("Port '%s' is not a valid port number" % (opt_port))
if not options.port:
raise BadUsage("You must supply a valid port.")
if opt_htpasswd_file is not None:
if not os.path.isfile(opt_htpasswd_file):
raise BadUsage("'%s' does not appear to be a valid htpasswd file."
% (opt_htpasswd_file))
if not has_crypt:
raise BadUsage("Unable to locate suitable `crypt' module for use "
"with --htpasswd-file option. If your Python "
"distribution does not include this module (as is "
"the case on many non-Unix platforms), consider "
"installing the `fcrypt' module instead (see "
"http://carey.geek.nz/code/python-fcrypt/).")
options.htpasswd_file = opt_htpasswd_file
if opt_config_file is not None:
if not os.path.isfile(opt_config_file):
raise BadUsage("'%s' does not appear to be a valid configuration file."
% (opt_config_file))
options.config_file = opt_config_file
if opt_host is not None:
options.host = opt_host
if opt_script_alias is not None:
options.script_alias = string.join(filter(None,
string.split(opt_script_alias,
'/')),
'/')
for repository in opt_repositories:
if not options.repositories.has_key('Development'):
rootname = 'Development'
else:
rootname = 'Repository%d' % (len(options.repositories.keys()) + 1)
options.repositories[rootname] = repository
except BadUsage, err:
badusage(str(err))
# Fork if we're in daemon mode.
if opt_daemon:
pid = os.fork()
if pid != 0:
sys.exit()
Options:
# Finaly, start the server.
if opt_gui:
gui(options.host, options.port)
else:
def ready(server):
print 'server ready at %s%s' % (server.url, options.script_alias)
serve(options.host, options.port, ready)
--config-file=PATH (-c) Use the file at PATH as the ViewVC configuration
file. If not specified, ViewVC will try to use
the configuration file in its installation tree;
otherwise, built-in default values are used.
(Not valid in GUI mode.)
--daemon (-d) Background the server process.
--host=HOST (-h) Start the server listening on HOST. You need
to provide the hostname if you want to
access the standalone server from a remote
machine. [default: %(host)s]
--port=PORT (-p) Start the server on the given PORT.
[default: %(port)d]
--repository=PATH (-r) Serve up the Subversion or CVS repository located
at PATH. This option may be used more than once.
--script-alias=PATH (-s) Specify the ScriptAlias, the artificial path
location that at which ViewVC appears to be
located. For example, if your ScriptAlias is
"cgi-bin/viewvc", then ViewVC will be accessible
at "http://%(host)s:%(port)s/cgi-bin/viewvc".
[default: %(script_alias)s]
--htpasswd-file=FILE Demand authentication from clients, validating
authentication credentials against FILE, which is
an Apache htpasswd file that employs CRYPT
encryption. (Sorry, no DIGEST support yet.)
--gui (-g) Pop up a graphical interface for serving and
testing ViewVC. NOTE: this requires a valid
X11 display connection.
""" % locals())
if __name__ == '__main__':
options = Options()
cli(sys.argv)
main(sys.argv)

View File

@@ -288,7 +288,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

View File

@@ -345,7 +345,43 @@
## 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
@@ -572,6 +608,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
@@ -595,7 +639,13 @@
#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
@@ -649,6 +699,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
@@ -769,6 +839,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
@@ -776,6 +854,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

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-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 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
@@ -420,6 +420,7 @@ 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
@@ -433,6 +434,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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 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,12 @@ class CheckinDatabase:
if file_id == None:
return None
commits_table = self._version >= 1 and 'commits' or 'checkins'
sql = "SELECT * 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 +542,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)
@@ -769,8 +783,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 +805,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 +814,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 +862,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 +894,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

@@ -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):
@@ -377,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 = [ ]
@@ -393,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)
@@ -417,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:
@@ -426,10 +430,12 @@ 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
@@ -449,6 +455,7 @@ def main(server, cfg, viewvc_link):
'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

@@ -233,9 +233,6 @@ class WsgiServer(Server):
self.header(status='301 Moved')
self._wsgi_write('This document is located <a href="%s">here</a>.' % url)
def escape(self, s, quote = None):
return cgi.escape(s, quote)
def getenv(self, name, value=None):
return self._environ.get(name, value)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-2010 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-2010 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-2010 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-2011 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 @@ 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-2011 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):

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 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
@@ -162,7 +167,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 +182,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:
@@ -1022,16 +1036,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-2011 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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 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]
@@ -152,7 +152,7 @@ class CCVSRepository(BaseCVSRepository):
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, "/")))

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 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-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -15,6 +15,7 @@
import os
import os.path
import re
import urllib
_re_url = re.compile('^(http|https|file|svn|svn\+[^:]+)://')
@@ -23,8 +24,20 @@ def canonicalize_rootpath(rootpath):
import svn.core
return svn.core.svn_path_canonicalize(rootpath)
except:
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/'):
return os.path.normpath(urllib.unquote(rootpath[16:]))
elif rootpath_lower.startswith('file:///'):
return os.path.normpath(urllib.unquote(rootpath[7:]))
if re.search(_re_url, rootpath):
return rootpath[-1] == '/' and rootpath[:-1] or rootpath
return rootpath.rstrip('/')
return os.path.normpath(rootpath)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 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
@@ -57,7 +57,7 @@ def client_log(url, start_rev, end_rev, log_limit, cross_copies,
client.svn_client_log4([url], start_rev, start_rev, end_rev,
log_limit, 1, not cross_copies, 0, None,
cb_func, ctx)
except NameError:
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):
@@ -203,6 +203,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
@@ -237,7 +241,7 @@ 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)
@@ -403,8 +407,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)

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 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
@@ -204,59 +204,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, revprops, 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)
@@ -427,6 +374,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
@@ -442,19 +393,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)
@@ -542,20 +488,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:
@@ -583,33 +528,56 @@ class LocalSubversionRepository(vclib.Repository):
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)
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)
##--- 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, author, date, revprops = _split_revprops(revprops)
# Optimization: If our caller doesn't care about the changed
# paths, and we don't need them to do authz determinations, let's
# get outta here.
if self.auth is None and not include_changed_paths:
return date, author, msg, revprops, None
# If we get here, then we either need the changed paths because we
# were asked for them, or we need them to do authorization checks.
# 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)
@@ -662,7 +630,8 @@ 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
@@ -674,24 +643,108 @@ 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 hasattr(change, 'copyfrom_known') and change.copyfrom_known:
copyfrom_path = change.copyfrom_path
copyfrom_rev = change.copyfrom_rev
else:
copyfrom_rev, copyfrom_path = fs.copied_from(fsroot, path)
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, 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, revprops, changedpaths.values()
else:
return date, author, msg, revprops, 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
@@ -704,40 +757,50 @@ class LocalSubversionRepository(vclib.Repository):
self._revinfo_cache[rev] = cached_info
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, revprops, 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, '/')
@@ -746,8 +809,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)
@@ -760,7 +826,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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -14,7 +14,7 @@
#
# -----------------------------------------------------------------------
__version__ = '1.1.8-dev'
__version__ = '1.1.12'
# this comes from our library; measure the startup time
import debug
@@ -79,10 +79,6 @@ _sticky_vars = [
'limit_changes',
]
# number of extra pages of information on either side of the current
# page to fetch (see dir_pagesize/log_pagesize configuration option)
EXTRA_PAGES = 3
# for reading/writing between a couple descriptors
CHUNK_SIZE = 8192
@@ -707,7 +703,6 @@ _legal_params = {
'mindate' : _re_validate_datetime,
'maxdate' : _re_validate_datetime,
'format' : _re_validate_alpha,
'limit' : _re_validate_number,
# for redirect_pathrev
'orig_path' : None,
@@ -1135,7 +1130,7 @@ class ViewVCHtmlFormatter:
linkified email address, with no more than MAXLEN characters
in the non-HTML-tag bits. If MAXLEN is 0, there is no maximum.
- the number of non-HTML-tag characters returned.
"""
"""
s = mobj.group(0)
trunc_s = maxlen and s[:maxlen] or s
return '<a href="mailto:%s">%s</a>' % (urllib.quote(s),
@@ -1236,45 +1231,51 @@ class ViewVCHtmlFormatter:
def _tokenize_text(self, s):
tokens = []
while s:
best_match = best_conv = best_userdata = None
for test in self._formatters:
match = test[0].search(s)
# If we find and match and (a) its our first one, or (b) it
# matches text earlier than our previous best match, or (c) it
# matches text at the same location as our previous best match
# but extends to cover more text than that match, then this is
# our new best match.
#
# Implied here is that when multiple formatters match exactly
# the same text, the first formatter in the registration list wins.
if match \
and ((best_match is None) \
or (match.start() < best_match.start())
or ((match.start() == best_match.start()) \
and (match.end() > best_match.end()))):
best_match = match
best_conv = test[1]
best_userdata = test[2]
# If we found a match...
if best_match:
# ... add any non-matching stuff first, then the matching bit.
start = best_match.start()
end = best_match.end()
if start > 0:
tokens.append(_item(match=s[:start],
# We could just have a "while s:" here instead of "for line: while
# line:", but for really large log messages with heavy
# tokenization, the cost in both performance and memory
# consumption of the approach taken was atrocious.
for line in string.split(string.replace(s, '\r\n', '\n'), '\n'):
line = line + '\n'
while line:
best_match = best_conv = best_userdata = None
for test in self._formatters:
match = test[0].search(line)
# If we find and match and (a) its our first one, or (b) it
# matches text earlier than our previous best match, or (c) it
# matches text at the same location as our previous best match
# but extends to cover more text than that match, then this is
# our new best match.
#
# Implied here is that when multiple formatters match exactly
# the same text, the first formatter in the registration list wins.
if match \
and ((best_match is None) \
or (match.start() < best_match.start())
or ((match.start() == best_match.start()) \
and (match.end() > best_match.end()))):
best_match = match
best_conv = test[1]
best_userdata = test[2]
# If we found a match...
if best_match:
# ... add any non-matching stuff first, then the matching bit.
start = best_match.start()
end = best_match.end()
if start > 0:
tokens.append(_item(match=line[:start],
converter=self.format_text,
userdata=None))
tokens.append(_item(match=best_match,
converter=best_conv,
userdata=best_userdata))
line = line[end:]
else:
# Otherwise, just add the rest of the string.
tokens.append(_item(match=line,
converter=self.format_text,
userdata=None))
tokens.append(_item(match=best_match,
converter=best_conv,
userdata=best_userdata))
s = s[end:]
else:
# Otherwise, just add the rest of the string.
tokens.append(_item(match=s,
converter=self.format_text,
userdata=None))
s = ''
line = ''
return tokens
@@ -1531,6 +1532,21 @@ class MarkupPipeWrapper:
if self.posttext:
ctx.fp.write(self.posttext)
_re_rewrite_escaped_url = re.compile('((http|https|ftp|file|svn|svn\+ssh)'
'(://[-a-zA-Z0-9%.~:_/]+)'
'((\?|\&amp;amp;|\&amp;|\&)'
'([-a-zA-Z0-9%.~:_]+)=([-a-zA-Z0-9%.~:_])+)*'
'(#([-a-zA-Z0-9%.~:_]+)?)?)')
def markup_escaped_urls(s):
# Return a copy of S with all URL references -- which are expected
# to be already HTML-escaped -- wrapped in <a href=""></a>.
def _url_repl(match_obj):
url = match_obj.group(0)
unescaped_url = string.replace(url, "&amp;amp;", "&amp;")
return "<a href=\"%s\">%s</a>" % (unescaped_url, url)
return re.sub(_re_rewrite_escaped_url, _url_repl, s)
def markup_stream_pygments(request, cfg, blame_data, fp, filename,
mime_type, encoding):
# Determine if we should use Pygments to highlight our output.
@@ -1551,13 +1567,15 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
blame_data = blame_source
lexer = None
use_pygments = cfg.options.enable_syntax_coloration
first_line = None
try:
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import ClassNotFound, \
get_lexer_by_name, \
get_lexer_for_mimetype, \
get_lexer_for_filename
get_lexer_for_filename, \
guess_lexer
if not encoding:
encoding = 'guess'
if cfg.options.detect_encoding:
@@ -1578,7 +1596,12 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
tabsize=cfg.options.tabsize,
stripnl=False)
except ClassNotFound:
use_pygments = 0
try:
first_line = fp.readline()
lexer = guess_lexer(first_line)
use_pygments = 1
except ClassNotFound:
use_pygments = 0
except ImportError:
use_pygments = 0
@@ -1594,17 +1617,23 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
def __getitem__(self, idx):
item = self.blame_source.__getitem__(idx)
item.text = string.expandtabs(item.text, self.tabsize)
item.text = markup_escaped_urls(item.text)
return item
return BlameSourceTabsizeWrapper(blame_source, cfg.options.tabsize)
else:
lines = []
line_no = 0
while 1:
line = fp.readline()
if first_line is not None:
line = first_line
first_line = None
else:
line = fp.readline()
if not line:
break
line_no = line_no + 1
line = sapi.escape(string.expandtabs(line, cfg.options.tabsize))
line = markup_escaped_urls(line)
item = vclib.Annotation(line, line_no, None, None, None, None)
item.diff_href = None
lines.append(item)
@@ -1622,6 +1651,7 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
self.line_no = 0
def write(self, buf):
### FIXME: Don't bank on write() being called once per line
buf = markup_escaped_urls(buf)
if self.has_blame_data:
self.blame_data[self.line_no].text = buf
else:
@@ -1631,7 +1661,7 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
self.blame_data.append(item)
self.line_no = self.line_no + 1
ps = PygmentsSink(blame_source)
highlight(fp.read(), lexer,
highlight((first_line or '') + fp.read(), lexer,
HtmlFormatter(nowrap=True,
classprefix="pygments-",
encoding='utf-8'), ps)
@@ -1725,7 +1755,7 @@ def markup_or_annotate(request, is_annotate):
# Is this a viewable image type?
if is_viewable_image(mime_type) \
and 'co' in cfg.options.allowed_views:
fp, revision = request.repos.openfile(path, rev)
fp, revision = request.repos.openfile(path, rev, {})
fp.close()
if check_freshness(request, None, revision, weak=1):
return
@@ -1748,7 +1778,7 @@ def markup_or_annotate(request, is_annotate):
except:
annotation = 'error'
fp, revision = request.repos.openfile(path, rev)
fp, revision = request.repos.openfile(path, rev, {'cvs_oldkeywords' : 1})
if check_freshness(request, None, revision, weak=1):
fp.close()
return
@@ -1903,13 +1933,27 @@ def view_roots(request):
rootnames = allroots.keys()
rootnames.sort(icmp)
for rootname in rootnames:
root_path, root_type, lastmod = allroots[rootname]
href = request.get_url(view_func=view_directory,
where='', pathtype=vclib.DIR,
params={'root': rootname}, escape=1)
if root_type == vclib.SVN:
log_href = request.get_url(view_func=view_log,
where='', pathtype=vclib.DIR,
params={'root': rootname}, escape=1)
else:
log_href = None
roots.append(_item(name=request.server.escape(rootname),
type=allroots[rootname][1],
path=allroots[rootname][0],
href=href))
type=root_type,
path=root_path,
author=lastmod and lastmod.author or None,
ago=lastmod and lastmod.ago or None,
date=lastmod and lastmod.date or None,
log=lastmod and lastmod.log or None,
short_log=lastmod and lastmod.short_log or None,
rev=lastmod and lastmod.rev or None,
href=href,
log_href=log_href))
data = common_template_data(request)
data.merge(ezt.TemplateData({
@@ -2233,10 +2277,11 @@ def paging(data, key, pagestart, local_name, pagesize):
# Slice
return data[key][pagestart:pageend]
def paging_sws(data, key, pagestart, local_name, pagesize, offset):
def paging_sws(data, key, pagestart, local_name, pagesize,
extra_pages, offset):
"""Implement sliding window-style paging."""
# Create the picklist
last_requested = pagestart + (EXTRA_PAGES * pagesize)
last_requested = pagestart + (extra_pages * pagesize)
picklist = data['picklist'] = []
has_more = ezt.boolean(0)
for i in range(0, len(data[key]), pagesize):
@@ -2365,9 +2410,9 @@ def view_log(request):
first = last = 0
if cfg.options.log_pagesize:
log_pagestart = int(request.query_dict.get('log_pagestart', 0))
first = log_pagestart - min(log_pagestart,
(EXTRA_PAGES * cfg.options.log_pagesize))
last = log_pagestart + ((EXTRA_PAGES + 1) * cfg.options.log_pagesize) + 1
total = cfg.options.log_pagesextra * cfg.options.log_pagesize
first = log_pagestart - min(log_pagestart, total)
last = log_pagestart + (total + cfg.options.log_pagesize) + 1
show_revs = request.repos.itemlog(request.path_parts, request.pathrev,
sortby, first, last - first, options)
@@ -2628,7 +2673,8 @@ def view_log(request):
request.get_form(params={'log_pagestart': None})
data['log_pagestart'] = int(request.query_dict.get('log_pagestart',0))
data['entries'] = paging_sws(data, 'entries', data['log_pagestart'],
'rev', cfg.options.log_pagesize, first)
'rev', cfg.options.log_pagesize,
cfg.options.log_pagesextra, first)
generate_page(request, "log", data)
@@ -2639,9 +2685,12 @@ def view_checkout(request):
if 'co' not in cfg.options.allowed_views:
raise debug.ViewVCException('Checkout view is disabled',
'403 Forbidden')
if request.pathtype != vclib.FILE:
raise debug.ViewVCException('Unsupported feature: checkout view on '
'directory', '400 Bad Request')
path, rev = _orig_path(request)
fp, revision = request.repos.openfile(path, rev)
fp, revision = request.repos.openfile(path, rev, {})
# The revision number acts as a strong validator.
if not check_freshness(request, None, revision):
@@ -2727,7 +2776,7 @@ def search_file(repos, path_parts, rev, search_re):
# Read in each line of a checked-out file, and then use re.search to
# search line.
fp = repos.openfile(path_parts, rev)[0]
fp = repos.openfile(path_parts, rev, {})[0]
matches = 0
while 1:
line = fp.readline()
@@ -2955,7 +3004,8 @@ class DiffSource:
class DiffSequencingError(Exception):
pass
def diff_parse_headers(fp, diff_type, rev1, rev2, sym1=None, sym2=None):
def diff_parse_headers(fp, diff_type, path1, path2, rev1, rev2,
sym1=None, sym2=None):
date1 = date2 = log_rev1 = log_rev2 = flag = None
header_lines = []
@@ -2984,15 +3034,15 @@ def diff_parse_headers(fp, diff_type, rev1, rev2, sym1=None, sym2=None):
if match:
date1 = match.group(1)
log_rev1 = match.group(2)
if sym1:
line = line[:-1] + ' %s\n' % sym1
line = '%s%s\t%s\t%s%s\n' % (f1, path1, date1, log_rev1,
sym1 and ' ' + sym1 or '')
elif line[:len(f2)] == f2:
match = _re_extract_rev.match(line)
if match:
date2 = match.group(1)
log_rev2 = match.group(2)
if sym2:
line = line[:-1] + ' %s\n' % sym2
line = '%s%s\t%s\t%s%s\n' % (f2, path2, date2, log_rev2,
sym2 and ' ' + sym2 or '')
parsing = 0
elif line[:3] == 'Bin':
flag = _RCSDIFF_IS_BINARY
@@ -3120,8 +3170,11 @@ def view_patch(request):
raise debug.ViewVCException('Invalid path(s) or revision(s) passed '
'to diff', '400 Bad Request')
date1, date2, flag, headers = diff_parse_headers(fp, diff_type, rev1, rev2,
sym1, sym2)
path_left = _path_join(p1)
path_right = _path_join(p2)
date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
path_left, path_right,
rev1, rev2, sym1, sym2)
server_fp = get_writeready_server_file(request, 'text/plain')
server_fp.write(headers)
@@ -3187,13 +3240,13 @@ def view_diff(request):
if (cfg.options.hr_intraline and idiff
and ((human_readable and idiff.sidebyside)
or (not human_readable and diff_type == vclib.UNIFIED))):
f1 = request.repos.openfile(p1, rev1)[0]
f1 = request.repos.openfile(p1, rev1, {})[0]
try:
lines_left = f1.readlines()
finally:
f1.close()
f2 = request.repos.openfile(p2, rev2)[0]
f2 = request.repos.openfile(p2, rev2, {})[0]
try:
lines_right = f2.readlines()
finally:
@@ -3217,6 +3270,7 @@ def view_diff(request):
changes = []
if fp:
date1, date2, flag, headers = diff_parse_headers(fp, diff_type,
path_left, path_right,
rev1, rev2, sym1, sym2)
if human_readable:
if flag is not None:
@@ -3406,7 +3460,7 @@ def generate_tarball(out, request, reldir, stack, dir_mtime=None):
### FIXME: Read the whole file into memory? Bad... better to do
### 2 passes.
fp = request.repos.openfile(rep_path + [file.name], request.pathrev)[0]
fp = request.repos.openfile(rep_path + [file.name], request.pathrev, {})[0]
contents = fp.read()
fp.close()
@@ -4055,7 +4109,6 @@ def view_query(request):
mindate = request.query_dict.get('mindate', '')
maxdate = request.query_dict.get('maxdate', '')
format = request.query_dict.get('format')
limit = int(request.query_dict.get('limit', 0))
limit_changes = int(request.query_dict.get('limit_changes',
cfg.options.limit_changes))
@@ -4125,29 +4178,32 @@ def view_query(request):
query.SetFromDateObject(mindate)
if maxdate is not None:
query.SetToDateObject(maxdate)
if limit:
query.SetLimit(limit)
elif format == 'rss':
# Set the admin-defined (via configuration) row limits. This is to avoid
# slamming the database server with a monster query.
if format == 'rss':
query.SetLimit(cfg.cvsdb.rss_row_limit)
else:
query.SetLimit(cfg.cvsdb.row_limit)
# run the query
db.RunQuery(query)
sql = request.server.escape(db.CreateSQLQueryString(query))
commit_list = query.GetCommitList()
row_limit_reached = query.GetLimitReached()
# gather commits
commits = []
plus_count = 0
minus_count = 0
mod_time = -1
if query.commit_list:
if commit_list:
files = []
limited_files = 0
current_desc = query.commit_list[0].GetDescriptionID()
current_rev = query.commit_list[0].GetRevision()
current_desc = commit_list[0].GetDescriptionID()
current_rev = commit_list[0].GetRevision()
dir_strip = _path_join(repos_dir)
for commit in query.commit_list:
for commit in commit_list:
commit_desc = commit.GetDescriptionID()
commit_rev = commit.GetRevision()
@@ -4216,7 +4272,7 @@ def view_query(request):
data = common_template_data(request)
data.merge(ezt.TemplateData({
'sql': sql,
'sql': request.server.escape(db.CreateSQLQueryString(query)),
'english_query': english_query(request),
'queryform_href': request.get_url(view_func=view_queryform, escape=1),
'backout_href': backout_href,
@@ -4225,6 +4281,7 @@ def view_query(request):
'show_branch': show_branch,
'querysort': querysort,
'commits': commits,
'row_limit_reached' : ezt.boolean(row_limit_reached),
'limit_changes': limit_changes,
'limit_changes_href': limit_changes_href,
'rss_link_href': request.get_url(view_func=view_query,
@@ -4267,11 +4324,26 @@ def list_roots(request):
for root in cfg.general.svn_roots.keys():
auth = setup_authorizer(cfg, request.username, root)
try:
vclib.svn.SubversionRepository(root, cfg.general.svn_roots[root], auth,
cfg.utilities, cfg.options.svn_config_dir)
repos = vclib.svn.SubversionRepository(root, cfg.general.svn_roots[root],
auth, cfg.utilities,
cfg.options.svn_config_dir)
lastmod = None
if cfg.options.show_roots_lastmod:
try:
repos.open()
youngest_rev = repos.youngest
date, author, msg, revprops, changes = repos.revinfo(youngest_rev)
date_str = make_time_string(date, cfg)
ago = html_time(request, date)
log = format_log(request, msg)
short_log = format_log(request, msg, maxlen=cfg.options.short_log_len)
lastmod = _item(ago=ago, author=author, date=date_str, log=log,
short_log=short_log, rev=str(youngest_rev))
except:
lastmod = None
except vclib.ReposNotFound:
continue
allroots[root] = [cfg.general.svn_roots[root], 'svn']
allroots[root] = [cfg.general.svn_roots[root], 'svn', lastmod]
# Add the viewable CVS roots
for root in cfg.general.cvs_roots.keys():
@@ -4281,7 +4353,7 @@ def list_roots(request):
cfg.utilities, cfg.options.use_rcsparse)
except vclib.ReposNotFound:
continue
allroots[root] = [cfg.general.cvs_roots[root], 'cvs']
allroots[root] = [cfg.general.cvs_roots[root], 'cvs', None]
return allroots
@@ -4356,14 +4428,35 @@ def locate_root(cfg, rootname):
return None, None
def load_config(pathname=None, server=None):
"""Load the ViewVC configuration file. SERVER is the server object
that will be using this configuration. Consult the environment for
the variable VIEWVC_CONF_PATHNAME and VIEWCVS_CONF_PATHNAME (its
legacy name) and, if set, use its value as the path of the
configuration file; otherwise, use PATHNAME (if provided). Failing
all else, use a hardcoded default configuration path."""
debug.t_start('load-config')
if pathname is None:
pathname = (os.environ.get("VIEWVC_CONF_PATHNAME")
or os.environ.get("VIEWCVS_CONF_PATHNAME")
or os.path.join(os.path.dirname(os.path.dirname(__file__)),
"viewvc.conf"))
# See if the environment contains overrides to the configuration
# path. If we have a SERVER object, consult its environment; use
# the OS environment otherwise.
env_pathname = None
if server is not None:
env_pathname = (server.getenv("VIEWVC_CONF_PATHNAME")
or server.getenv("VIEWCVS_CONF_PATHNAME"))
else:
env_pathname = (os.environ.get("VIEWVC_CONF_PATHNAME")
or os.environ.get("VIEWCVS_CONF_PATHNAME"))
# Try to find the configuration pathname by searching these ordered
# locations: the environment, the passed-in PATHNAME, the hard-coded
# default.
pathname = (env_pathname
or pathname
or os.path.join(os.path.dirname(os.path.dirname(__file__)),
"viewvc.conf"))
# Load the configuration!
cfg = config.Config()
cfg.set_defaults()
cfg.load_config(pathname, server and server.getenv("HTTP_HOST"))

View File

@@ -85,9 +85,12 @@ numbers, and not literal):
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:
http://viewvc.tigris.org/servlets/ProjectDocumentList?folderID=6004
Also, drop a copy of the archive files into the root directory of
the viewvc.org website (unversioned).
15. 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
@@ -104,6 +107,9 @@ numbers, and not literal):
(and possibly, minor or major) release. (For the Milestone sort
key, use a packed integer XXYYZZ: 1.0.3 == 10003, 2.11.4 == 21104.)
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, and post similar announcements to other places interested
in this sort of stuff, such as Freshmeat (http://www.freshmeat.net).

View File

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

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

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

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

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