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

Compare commits

..

26 Commits

Author SHA1 Message Date
cmpilato
d228de45d7 Tag the 1.1.14 final release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.14@2749 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 12:45:20 +00:00
cmpilato
5a51470cbc Let's try to release 1.1.14 today.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2748 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 12:44:11 +00:00
cmpilato
acc0783468 Merge from trunk r2746, whose log message reads like so:
Fix issue #246 ("Add support for issue tracker links in commit-log
   viewer").
   
   NOTE: There are some limitations here, most prominantly that commas
   can't be used in the regular expressions which define replacements,
   and that only match groupings 0-9 can be used in the replacement
   format string.
   
   * conf/viewvc.conf.dist
     (custom_log_formatting): New configuration option.
   
   * lib/config.py
     (Config._force_multi_value, Config.set_defaults): Add handling of
       new 'custom_log_formatting' option.
   
   * lib/viewvc.py
     (ViewVCHtmlFormatter.format_custom_url): New formatter callback.
     (LogFormatter.get): Register the new formatter callback for rules
       found in the 'custom_log_formatting' option value.

Also:

* CHANGES:
  Note this change.


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

Also:

* CHANGES
  Note this change.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2742 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-04-17 17:54:45 +00:00
cmpilato
ca1bd67b5d Merge from trunk r2738, whose log message read like so:
Fix issue #505 ("Username case normalization breaks when browsing
   anonymously").
   
   * lib/vcauth/svnauthz/__init__.py
     (ViewVCAuthorizer.__init__): Don't try to convert a None username to
       lower- or upper-case.

Also:

* CHANGES
  Note this change.

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

   Fix issue #504 ("annotate view fails on filenames with colon (:)
   characters in their names"), another in a long string of annotate view
   brokennesses.  (Is that word?)  This time, the problem was a disparity
   between Python's urllib encoding and what Subversion deems canonical.

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

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

Also:

* CHANGES
  Note this change.

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

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

   * lib/vclib/svn/svn_repos.py
     (_rootpath2url): Try a different approach that should actually
       handle spaces in the "in-repos" path, too, and maybe (if I'm super
       lucky) also work on Windows.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2708 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-03 15:22:31 +00:00
cmpilato
f43117b10f Merge from trunk r2702, whose log message read like so:
* lib/viewvc.py
     (): Stop importing the pygments stuff at the module-global scope,
       and go back to doing so...
     (markup_stream): ...here.
     (markup_or_annotate): No longer consider access to Pygments when
       making the first pass at colorizing -- if we don't have it,
       markup_stream() will fail quickly and we can take the second
       (non-colorizing) pass.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2703 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-12 20:07:58 +00:00
cmpilato
a025237f7e Tweak CHANGES line in light of r2700.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2701 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-06 14:50:02 +00:00
cmpilato
228db2fadb Merge from trunk r2679 and r2699, which is the reintroduction of the
issue #495 ("Syntax highlight/colorize scripts without extensions")
feature, plus a fix to help the prefer_markup flag get set more
accurately for the markup and annotate views.


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

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2689 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 18:36:32 +00:00
cmpilato
b808d5d1e8 Merge from trunk r2686.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2687 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-12-02 18:30:31 +00:00
cmpilato
f843c054b7 Merge from trunk r2684, whose log message read like so:
Revert r2657, r2680, and r2681, effectively removing support for
   Pygments' lexer guessing functionality (closing the REOPENED bug issue
   #501, and necessitating that feature issue #495 be itself REOPENED).

Also:

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

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

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

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2682 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-29 15:40:59 +00:00
cmpilato
bc8f3bdd4f Merge from trunk r2674, whose log message read like so:
Fix annotate views of files in Subversion repository whose paths
   contain non-URI-safe characters (spaces, non-ASCII stuff, etc.)
   
   * lib/vclib/svn/svn_repos.py
     (_rootpath2url): Use urllib.pathname2url to URL-encode the path
       portion of the URL we are building.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2675 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-14 21:34:14 +00:00
cmpilato
a6fcab67b0 Merge from trunk r2671, whose log message went a little something like this:
Fix issue #499 ("svndbadmin fails on deleted files").
   
   * bin/svndbadmin
     (SvnRev.__init__): Update the logic used to calculate change types
       and diff objects to no longer assume that deleted paths have None
       for their change.path.  (Subversion's 1.7 bindings always populate
       change.path.)

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2672 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-14 20:16:01 +00:00
cmpilato
4069208316 Merge from trunk r2668 (a doc tweak).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2669 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-03 14:44:33 +00:00
cmpilato
cfaa30b40f Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2667 8cb11bc2-c004-0410-86c3-e597b4017df7
2011-11-03 14:40:51 +00:00
56 changed files with 499 additions and 257 deletions

16
CHANGES
View File

@@ -1,3 +1,18 @@
Version 1.1.14 (released 12-Jun-2012)
* fix annotation of svn files with non-URI-safe paths (issue #504)
* handle file:/// Subversion rootpaths as local roots (issue #446)
* fix bug caused by trying to case-normalize anon usernames (issue #505)
* speed up log handling by reusing tokenization results (issue #506)
* add support for custom review log markup rules (issue #429)
Version 1.1.13 (released 23-Jan-2012)
* fix svndbadmin failure on deleted paths under Subversion 1.7 (issue #499)
* fix annotation of files in svn roots with non-URI-safe paths
* fix stray annotation warning in markup display of images
* more gracefully handle attempts to display binary content (issue #501)
Version 1.1.12 (released 03-Nov-2011)
* fix path display in patch and certain diff views (issue #485)
@@ -31,7 +46,6 @@ Version 1.1.9 (released 18-Feb-2011)
* 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)

View File

@@ -15,7 +15,7 @@
<blockquote>
<p><strong>Copyright &copy; 1999-2011 The ViewCVS Group. All rights
<p><strong>Copyright &copy; 1999-2012 The ViewCVS Group. All rights
reserved.</strong></p>
<p>By using ViewVC, you agree to the terms and conditions set forth
@@ -62,6 +62,7 @@
<li>March 18, 2009 &mdash; copyright years updated</li>
<li>March 29, 2010 &mdash; copyright years updated</li>
<li>February 18, 2011 &mdash; copyright years updated</li>
<li>January 23, 2012 &mdash; copyright years updated</li>
</ul>
</body>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 2004-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2004-2012 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2004-2007 James Henstridge
#
# By using this file, you agree to the terms and conditions set forth in
@@ -153,7 +153,7 @@ class SvnRev:
fsroot = self._get_root_for_rev(rev)
# find changes in the revision
editor = svn.repos.RevisionChangeCollector(repo.fs, rev)
editor = svn.repos.ChangeCollector(repo.fs, fsroot)
e_ptr, e_baton = svn.delta.make_editor(editor)
svn.repos.svn_repos_replay(fsroot, e_ptr, e_baton)
@@ -164,21 +164,33 @@ class SvnRev:
continue
# deal with the change types we handle
action = None
base_root = None
base_path = change.base_path
if change.base_path:
base_root = self._get_root_for_rev(change.base_rev)
if not change.path:
# figure out what kind of change this is, and get a diff
# object for it. note that prior to 1.4 Subversion's
# bindings didn't give us change.action, but that's okay
# because back then deleted paths always had a change.path
# of None.
if hasattr(change, 'action') \
and change.action == svn.repos.CHANGE_ACTION_DELETE:
action = 'remove'
elif not change.path:
action = 'remove'
elif change.added:
action = 'add'
else:
action = 'change'
diffobj = svn.fs.FileDiff(base_root and base_root or None,
base_root and change.base_path or None,
change.path and fsroot or None,
change.path and change.path or None)
if action == 'remove':
diffobj = svn.fs.FileDiff(base_root, base_path, None, None)
else:
diffobj = svn.fs.FileDiff(base_root, base_path,
fsroot, change.path)
diff_fp = diffobj.get_pipe()
plus, minus = _get_diff_counts(diff_fp)
self.changes.append((path, action, plus, minus))

View File

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

View File

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

View File

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

View File

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

View File

@@ -86,8 +86,8 @@
## cvs_roots: Specifies each of the CVS roots on your system and
## assigns names to them. Each root should be given by a "name: path"
## value. Multiple roots should be separated by commas and can be
## placed on separate lines.
## value (where the path is an absolute filesystem path). Multiple roots
## should be separated by commas and can be placed on separate lines.
##
## Example:
## cvs_roots = cvsroot: /opt/cvs/repos1,
@@ -97,8 +97,13 @@
## svn_roots: Specifies each of the Subversion roots (repositories) on
## your system and assigns names to them. Each root should be given by
## a "name: path" value. Multiple roots should be separated by commas
## and can be placed on separate lines.
## a "name: path" value (where the path is an absolute filesystem path).
## Multiple roots should be separated by commas and can be placed on
## separate lines.
##
## NOTE: ViewVC offers *experimental* support for displaying remote
## Subversion repositories. Simply use the repository's URL instead
## of a local filesystem path when defining the root.
##
## Example:
## svn_roots = svnrepos: /opt/svn/,
@@ -109,7 +114,8 @@
## root_parents: Specifies a list of directories under which any
## number of repositories may reside. You can specify multiple root
## parents separated by commas or new lines, each of which is of the
## form "path: type" (where type is either "cvs" or "svn").
## form "path: type" (where the type is either "cvs" or "svn", and
## the path is an absolute filesystem path).
##
## Rather than force you to add a new entry to 'cvs_roots' or
## 'svn_roots' each time you create a new repository, ViewVC rewards
@@ -427,6 +433,26 @@
##
#mangle_email_addresses = 0
## custom_log_formatting: Specifies mappings of regular expressions to
## substitution format strings used to URL-ize strings found in
## revision log messages. Multiple mappings (specified as
## REGEXP:FORMATSTRING) may be defined, separated by commas.
##
## NOTE: Due to a limitation of the configuration format, commas may
## not be used in the regular expression portion of each mapping.
## Commas "in the raw" may not be used in the format string portion,
## either, but you can probably use the URI-encoded form of the comma
## ("%2C") instead with no ill side-effects. If you must specify a
## colon character in either the regular expression or the format
## string, escape it with a preceding backslash ("\:").
##
## Example:
## custom_log_formatting =
## artf[0-9]+ : http://example.com/tracker?id=\0,
## issue ([0-9]+) : http://example.com/bug?id=\1&opts=full%2csortby=id
##
#custom_log_formatting =
## default_file_view: "log", "co", or "markup"
## Controls whether the default view for file URLs is a checkout view or
## a log view. "log" is the default for backwards compatibility with old

View File

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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2010 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
#
# By using this file, you agree to the terms and conditions set forth in
@@ -74,7 +74,8 @@ class HTMLBlameSource:
self.path_parts = path_parts
self.diff_url = diff_url
self.include_url = include_url
self.annotation, self.revision = self.repos.annotate(path_parts, opt_rev)
self.annotation, self.revision = self.repos.annotate(path_parts, opt_rev,
True)
def __getitem__(self, idx):
item = self.annotation.__getitem__(idx)

View File

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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2012 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
@@ -112,6 +112,7 @@ class Config:
_force_multi_value = (
# Configuration values with multiple, comma-separated values.
'allowed_views',
'custom_log_formatting',
'cvs_roots',
'kv_files',
'languages',
@@ -398,6 +399,7 @@ class Config:
self.options.allowed_views = ['annotate', 'diff', 'markup', 'roots']
self.options.authorizer = None
self.options.mangle_email_addresses = 0
self.options.custom_log_formatting = []
self.options.default_file_view = "log"
self.options.http_expiration_time = 600
self.options.generate_etags = 1

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 2006-2011 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2006-2012 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -34,9 +34,9 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
# See if the admin wants us to do case normalization of usernames.
self.force_username_case = params.get('force_username_case')
if self.force_username_case == "upper":
self.username = username.upper()
self.username = username and string.upper(username) or username
elif self.force_username_case == "lower":
self.username = username.lower()
self.username = username and string.lower(username) or username
elif not self.force_username_case:
self.username = username
else:

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2012 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
@@ -170,12 +170,19 @@ class Repository:
Return value is a python file object
"""
def annotate(self, path_parts, rev):
"""Return a list of annotate file content lines and a revision.
def annotate(self, path_parts, rev, include_text=False):
"""Return a list of Annotation object, sorted by their
"line_number" components, which describe the lines of given
version of a file.
The result is a list of Annotation objects, sorted by their
line_number components.
"""
The file path is specified as a list of components, relative to
the root of the repository. e.g. ["subdir1", "subdir2", "filename"]
rev is the revision of the item to return information about.
If include_text is true, populate the Annotation objects' "text"
members with the corresponding line of file content; otherwise,
leave that member set to None."""
def revinfo(self, rev):
"""Return information about a global revision

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -14,6 +14,7 @@ import os.path
def canonicalize_rootpath(rootpath):
assert os.path.isabs(rootpath)
return os.path.normpath(rootpath)
@@ -22,6 +23,7 @@ def expand_root_parent(parent_path):
# "CVSROOT/config" is added the set of returned roots. Or, if the
# PARENT_PATH itself contains a child "CVSROOT/config", then all its
# subdirectories are returned as roots.
assert os.path.isabs(parent_path)
roots = {}
subpaths = os.listdir(parent_path)
cvsroot = os.path.exists(os.path.join(parent_path, "CVSROOT", "config"))

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2012 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
@@ -332,13 +332,13 @@ class BinCVSRepository(BaseCVSRepository):
args = rcs_args
return popen.popen(cmd, args, mode, capture_err)
def annotate(self, path_parts, rev=None):
def annotate(self, path_parts, rev=None, include_text=False):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
from vclib.ccvs import blame
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text)
return source, source.revision
def revinfo(self, rev):

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*-python-*-
#
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
#
# By using this file, you agree to the terms and conditions set forth in
@@ -414,7 +414,7 @@ class CVSParser(rcsparse.Sink):
class BlameSource:
def __init__(self, rcs_file, opt_rev=None):
def __init__(self, rcs_file, opt_rev=None, include_text=False):
# Parse the CVS file
parser = CVSParser()
revision = parser.parse_cvs_file(rcs_file, opt_rev)
@@ -428,6 +428,7 @@ class BlameSource:
self.lines = lines
self.num_lines = count
self.parser = parser
self.include_text = include_text
# keep track of where we are during an iteration
self.idx = -1
@@ -447,6 +448,8 @@ class BlameSource:
line_number = idx + 1
author = self.parser.revision_author[rev]
thisline = self.lines[idx]
if not self.include_text:
thisline = None
### TODO: Put a real date in here.
item = vclib.Annotation(thisline, line_number, rev, prev_rev, author, None)
self.last = item

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2012 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
@@ -142,11 +142,11 @@ class CCVSRepository(BaseCVSRepository):
return vclib._diff_fp(temp1, temp2, info1, info2,
self.utilities.diff or 'diff', diff_args)
def annotate(self, path_parts, rev=None):
def annotate(self, path_parts, rev=None, include_text=False):
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file."
% (string.join(path_parts, "/")))
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text)
return source, source.revision
def revinfo(self, rev):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2012 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
@@ -20,7 +20,10 @@ import re
import tempfile
import time
import urllib
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev, _fix_subversion_exception, _split_revprops
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, \
_compare_paths, _path_parts, _cleanup_path, \
_rev2optrev, _fix_subversion_exception, \
_split_revprops, _canonicalize_path
from svn import core, delta, client, wc, ra
@@ -338,7 +341,7 @@ class RemoteSubversionRepository(vclib.Repository):
_rev2optrev(rev), 0, self.ctx)
return pairs and pairs[0][1] or {}
def annotate(self, path_parts, rev):
def annotate(self, path_parts, rev, include_text=False):
path = self._getpath(path_parts)
if self.itemtype(path_parts, rev) != vclib.FILE: # does auth-check
raise vclib.Error("Path '%s' is not a file." % path)
@@ -352,12 +355,13 @@ class RemoteSubversionRepository(vclib.Repository):
prev_rev = None
if revision > 1:
prev_rev = revision - 1
blame_data.append(vclib.Annotation(line, line_no+1, revision, prev_rev,
if not include_text:
line = None
blame_data.append(vclib.Annotation(line, line_no + 1, revision, prev_rev,
author, None))
client.svn_client_blame(url, _rev2optrev(1), _rev2optrev(rev),
_blame_cb, self.ctx)
return blame_data, rev
def revinfo(self, rev):
@@ -420,7 +424,8 @@ class RemoteSubversionRepository(vclib.Repository):
def _geturl(self, path=None):
if not path:
return self.rootpath
return self.rootpath + '/' + urllib.quote(path, "/*~")
path = self.rootpath + '/' + urllib.quote(path)
return _canonicalize_path(path)
def _get_dirents(self, path, rev):
"""Return a 2-type of dirents and locks, possibly reading/writing

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2012 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewVC
@@ -22,6 +22,7 @@ import time
import tempfile
import popen
import re
import urllib
from svn import fs, repos, core, client, delta
@@ -42,7 +43,14 @@ def _fix_subversion_exception(e):
e.apr_err = e[1]
if not hasattr(e, 'message'):
e.message = e[0]
### Pre-1.4 Subversion doesn't have svn_path_canonicalize()
def _canonicalize_path(path):
try:
return core.svn_path_canonicalize(path)
except AttributeError:
return path
def _allow_all(root, path, pool):
"""Generic authz_read_func that permits access to all paths"""
return 1
@@ -108,11 +116,16 @@ def _rev2optrev(rev):
def _rootpath2url(rootpath, path):
rootpath = os.path.abspath(rootpath)
if rootpath and rootpath[0] != '/':
rootpath = '/' + rootpath
drive, rootpath = os.path.splitdrive(rootpath)
if os.sep != '/':
rootpath = string.replace(rootpath, os.sep, '/')
return 'file://' + string.join([rootpath, path], "/")
rootpath = urllib.quote(rootpath)
path = urllib.quote(path)
if drive:
url = 'file:///' + drive + rootpath + '/' + path
else:
url = 'file://' + rootpath + '/' + path
return _canonicalize_path(url)
# Given a dictionary REVPROPS of revision properties, pull special
@@ -282,10 +295,11 @@ class FileContentsPipe:
class BlameSource:
def __init__(self, local_url, rev, first_rev, config_dir):
def __init__(self, local_url, rev, first_rev, include_text, config_dir):
self.idx = -1
self.first_rev = first_rev
self.blame_data = []
self.include_text = include_text
ctx = client.svn_client_create_context()
core.svn_config_ensure(config_dir)
@@ -306,6 +320,8 @@ class BlameSource:
prev_rev = None
if rev > self.first_rev:
prev_rev = rev - 1
if not self.include_text:
text = None
self.blame_data.append(vclib.Annotation(text, line_no + 1, rev,
prev_rev, author, None))
@@ -521,7 +537,7 @@ class LocalSubversionRepository(vclib.Repository):
fsroot = self._getroot(rev)
return fs.node_proplist(fsroot, path)
def annotate(self, path_parts, rev):
def annotate(self, path_parts, rev, include_text=False):
path = self._getpath(path_parts)
path_type = self.itemtype(path_parts, rev) # does auth-check
if path_type != vclib.FILE:
@@ -532,8 +548,8 @@ class LocalSubversionRepository(vclib.Repository):
{'svn_cross_copies': 1})
youngest_rev, youngest_path = history[0]
oldest_rev, oldest_path = history[-1]
source = BlameSource(_rootpath2url(self.rootpath, path),
youngest_rev, oldest_rev, self.config_dir)
source = BlameSource(_rootpath2url(self.rootpath, path), youngest_rev,
oldest_rev, include_text, self.config_dir)
return source, youngest_rev
def revinfo(self, rev):

View File

@@ -1,6 +1,6 @@
# -*-python-*-
#
# Copyright (C) 1999-2011 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2012 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.12'
__version__ = '1.1.14'
# this comes from our library; measure the startup time
import debug
@@ -1099,6 +1099,29 @@ _re_rewrite_email = re.compile('([-a-zA-Z0-9_.\+]+)@'
# Matches revision references
_re_rewrite_svnrevref = re.compile(r'\b(r|rev #?|revision #?)([0-9]+)\b')
class ViewVCHtmlFormatterTokens:
def __init__(self, tokens):
self.tokens = tokens
def get_result(self, maxlen=0):
"""Format the tokens per the registered set of formatters, and
limited to MAXLEN visible characters (or unlimited if MAXLEN is
0). Return a 3-tuple containing the formatted result string, the
number of visible characters in the result string, and a boolean
flag indicating whether or not S was truncated."""
out = ''
out_len = 0
for token in self.tokens:
chunk, chunk_len = token.converter(token.match, token.userdata, maxlen)
out = out + chunk
out_len = out_len + chunk_len
if maxlen:
maxlen = maxlen - chunk_len
if maxlen <= 0:
return out, out_len, 1
return out, out_len, 0
class ViewVCHtmlFormatter:
"""Format a string as HTML-encoded output with customizable markup
rules, for example turning strings that look like URLs into anchor links.
@@ -1184,6 +1207,30 @@ class ViewVCHtmlFormatter:
sapi.escape(trunc_s)), \
len(trunc_s)
def format_custom_url(self, mobj, userdata, maxlen=0):
"""Return a 2-tuple containing:
- the text represented by MatchObject MOBJ, formatted as an
linkified URL created by substituting match groups 0-9 into
USERDATA (which is a format string that uses \N to
represent the substitution locations) and with no more than
MAXLEN characters in the non-HTML-tag portions. If MAXLEN
is 0, there is no maximum.
- the number of characters returned.
"""
format = userdata
text = mobj.group(0)
url = format
for i in range(9):
try:
repl = mobj.group(i)
except:
repl = ''
url = url.replace('\%d' % (i), repl)
trunc_s = maxlen and text[:maxlen] or text
return '<a href="%s">%s</a>' % (sapi.escape(url),
sapi.escape(trunc_s)), \
len(trunc_s)
def format_text(self, s, unused, maxlen=0):
"""Return a 2-tuple containing:
- the text S, HTML-escaped, containing no more than MAXLEN
@@ -1216,20 +1263,14 @@ class ViewVCHtmlFormatter:
"""
out = ''
out_len = 0
for token in self._tokenize_text(s):
chunk, chunk_len = token.converter(token.match, token.userdata, maxlen)
out = out + chunk
out_len = out_len + chunk_len
if maxlen:
maxlen = maxlen - chunk_len
if maxlen <= 0:
return out, out_len, 1
return out, out_len, 0
tokens = self.tokenize_text(s)
return tokens.get_result()
def _entity_encode(self, s):
return string.join(map(lambda x: '&#%d;' % (ord(x)), s), '')
def _tokenize_text(self, s):
def tokenize_text(self, s):
"""Return a ViewVCHtmlFormatterTokens object containing the tokens
created when parsing the string S. Callers can use that object's
get_result() function to retrieve HTML-formatted text.
"""
tokens = []
# We could just have a "while s:" here instead of "for line: while
# line:", but for really large log messages with heavy
@@ -1276,36 +1317,79 @@ class ViewVCHtmlFormatter:
converter=self.format_text,
userdata=None))
line = ''
return tokens
return ViewVCHtmlFormatterTokens(tokens)
def _entity_encode(self, s):
return string.join(map(lambda x: '&#%d;' % (ord(x)), s), '')
def format_log(request, log, maxlen=0, htmlize=1):
if not log:
return log
class LogFormatter:
def __init__(self, request, log):
self.request = request
self.log = log or ''
self.tokens = None
self.cache = {} # (maxlen, htmlize) => resulting_log
cfg = request.cfg
if htmlize:
lf = ViewVCHtmlFormatter()
lf.add_formatter(_re_rewrite_url, lf.format_url)
if request.roottype == 'svn':
def revision_to_url(rev):
return request.get_url(view_func=view_revision,
params={'revision': rev},
escape=1)
lf.add_formatter(_re_rewrite_svnrevref, lf.format_svnrevref,
revision_to_url)
if cfg.options.mangle_email_addresses == 2:
lf.add_formatter(_re_rewrite_email, lf.format_email_truncated)
elif cfg.options.mangle_email_addresses == 1:
lf.add_formatter(_re_rewrite_email, lf.format_email_obfuscated)
def get(self, maxlen=0, htmlize=1):
cfg = self.request.cfg
# Prefer the cache.
if self.cache.has_key((maxlen, htmlize)):
return self.cache[(maxlen, htmlize)]
# If we are HTML-izing...
if htmlize:
# ...and we don't yet have ViewVCHtmlFormatter() object tokens...
if not self.tokens:
# ... then get them.
lf = ViewVCHtmlFormatter()
# Rewrite URLs.
lf.add_formatter(_re_rewrite_url, lf.format_url)
# Rewrite Subversion revision references.
if self.request.roottype == 'svn':
def revision_to_url(rev):
return self.request.get_url(view_func=view_revision,
params={'revision': rev},
escape=1)
lf.add_formatter(_re_rewrite_svnrevref, lf.format_svnrevref,
revision_to_url)
# Rewrite email addresses.
if cfg.options.mangle_email_addresses == 2:
lf.add_formatter(_re_rewrite_email, lf.format_email_truncated)
elif cfg.options.mangle_email_addresses == 1:
lf.add_formatter(_re_rewrite_email, lf.format_email_obfuscated)
else:
lf.add_formatter(_re_rewrite_email, lf.format_email)
# Add custom rewrite handling per configuration.
for rule in cfg.options.custom_log_formatting:
rule = rule.replace('\\:', '\x01')
regexp, format = map(lambda x: x.strip(), rule.split(':', 1))
regexp = regexp.replace('\x01', ':')
format = format.replace('\x01', ':')
lf.add_formatter(re.compile(regexp), lf.format_custom_url, format)
# Tokenize the log message.
self.tokens = lf.tokenize_text(self.log)
# Use our formatter to ... you know ... format.
log, log_len, truncated = self.tokens.get_result(maxlen)
result_log = log + (truncated and '&hellip;' or '')
# But if we're not HTML-izing...
else:
lf.add_formatter(_re_rewrite_email, lf.format_email)
log, log_len, truncated = lf.get_result(log, maxlen)
return log + (truncated and '&hellip;' or '')
else:
if cfg.options.mangle_email_addresses == 2:
log = re.sub(_re_rewrite_email, r'\1@...', log)
return maxlen and log[:maxlen] or log
# ...then do much more simplistic transformations as necessary.
if cfg.options.mangle_email_addresses == 2:
log = re.sub(_re_rewrite_email, r'\1@...', log)
result_log = maxlen and log[:maxlen] or log
# In either case, populate the cache and return the results.
self.cache[(maxlen, htmlize)] = result_log
return result_log
_time_desc = {
1 : 'second',
@@ -1547,28 +1631,29 @@ def markup_escaped_urls(s):
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.
# Reasons not to include a) being told not to by the configuration,
# b) not being able to import the Pygments modules, and c) Pygments
# not having a lexer for our file's format.
blame_source = []
if blame_data:
for i in blame_data:
i.text = sapi.escape(i.text)
i.diff_href = None
if i.prev_rev:
i.diff_href = request.get_url(view_func=view_diff,
params={'r1': i.prev_rev,
'r2': i.rev},
escape=1, partial=1)
blame_source.append(i)
blame_data = blame_source
lexer = None
use_pygments = cfg.options.enable_syntax_coloration
first_line = None
try:
def markup_stream(request, cfg, blame_data, file_lines, filename,
mime_type, encoding, colorize):
"""Return the contents of a versioned file as a list of
vclib.Annotation objects, each representing one line of the file's
contents. Use BLAME_DATA as the annotation information for the file
if provided. Use FILE_LINES as the lines of file content text
themselves. MIME_TYPE is the MIME content type of the file;
ENCODING is its character encoding. If COLORIZE is true, attempt to
apply syntax coloration to the file contents, and use the
HTML-marked-up results as the text in the return vclib.Annotation
objects."""
# Nothing to mark up? So be it.
if not file_lines:
return []
# Determine if we should (and can) use Pygments to highlight our
# output. Reasons not to include a) being told not to by the
# configuration, b) not being able to import the Pygments modules,
# and c) Pygments not having a lexer for our file's format.
pygments_lexer = None
if colorize:
from pygments import highlight
from pygments.formatters import HtmlFormatter
from pygments.lexers import ClassNotFound, \
@@ -1584,59 +1669,50 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
encoding = 'chardet'
except (SyntaxError, ImportError):
pass
try:
lexer = get_lexer_for_mimetype(mime_type,
encoding=encoding,
tabsize=cfg.options.tabsize,
stripnl=False)
except ClassNotFound:
try:
lexer = get_lexer_for_filename(filename,
encoding=encoding,
tabsize=cfg.options.tabsize,
stripnl=False)
except ClassNotFound:
try:
first_line = fp.readline()
lexer = guess_lexer(first_line)
use_pygments = 1
except ClassNotFound:
use_pygments = 0
except ImportError:
use_pygments = 0
# If we aren't going to be highlighting anything, just return the
# BLAME_SOURCE. If there's no blame_source, we'll generate a fake
# one from the file contents we fetch with PATH and REV.
if not use_pygments:
if blame_source:
class BlameSourceTabsizeWrapper:
def __init__(self, blame_source, tabsize):
self.blame_source = blame_source
self.tabsize = cfg.options.tabsize
def __getitem__(self, idx):
item = self.blame_source.__getitem__(idx)
item.text = string.expandtabs(item.text, self.tabsize)
item.text = markup_escaped_urls(item.text)
return item
return BlameSourceTabsizeWrapper(blame_source, cfg.options.tabsize)
else:
lines = []
line_no = 0
while 1:
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)
# First, see if there's a Pygments lexer associated with MIME_TYPE.
if mime_type:
try:
pygments_lexer = get_lexer_for_mimetype(mime_type,
encoding=encoding,
tabsize=cfg.options.tabsize,
stripnl=False)
except ClassNotFound:
pygments_lexer = None
# If we've no lexer thus far, try to find one based on the FILENAME.
if not pygments_lexer:
try:
pygments_lexer = get_lexer_for_filename(filename,
encoding=encoding,
tabsize=cfg.options.tabsize,
stripnl=False)
except ClassNotFound:
pygments_lexer = None
# Still no lexer? If we've reason to believe this is a text
# file, try to guess the lexer based on the file's content.
if not pygments_lexer and is_text(mime_type) and file_lines:
try:
pygments_lexer = guess_lexer(file_lines[0])
except ClassNotFound:
pygments_lexer = None
# If we aren't highlighting, just return an amalgamation of the
# BLAME_DATA (if any) and the FILE_LINES.
if not pygments_lexer:
lines = []
for i in range(len(file_lines)):
line = file_lines[i]
line = sapi.escape(string.expandtabs(line, cfg.options.tabsize))
line = markup_escaped_urls(line)
if blame_data:
blame_item = blame_data[i]
blame_item.text = line
else:
blame_item = vclib.Annotation(line, i + 1, None, None, None, None)
blame_item.diff_href = None
lines.append(blame_item)
return lines
# If we get here, we're highlighting something.
@@ -1660,8 +1736,9 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename,
item.diff_href = None
self.blame_data.append(item)
self.line_no = self.line_no + 1
ps = PygmentsSink(blame_source)
highlight((first_line or '') + fp.read(), lexer,
ps = PygmentsSink(blame_data)
highlight(string.join(file_lines, ''), pygments_lexer,
HtmlFormatter(nowrap=True,
classprefix="pygments-",
encoding='utf-8'), ps)
@@ -1700,7 +1777,8 @@ def get_itemprops(request, path_parts, rev):
propnames.sort()
props = []
for name in propnames:
value = format_log(request, itemprops[name])
lf = LogFormatter(request, itemprops[name])
value = lf.get(maxlen=0, htmlize=1)
undisplayable = ezt.boolean(0)
# skip non-utf8 property names
try:
@@ -1759,34 +1837,71 @@ def markup_or_annotate(request, is_annotate):
fp.close()
if check_freshness(request, None, revision, weak=1):
return
annotation = 'binary'
if is_annotate:
annotation = 'binary'
image_src_href = request.get_url(view_func=view_checkout,
params={'revision': rev}, escape=1)
# Not a viewable image.
else:
blame_source = None
blame_data = None
# If this was an annotation request, try to annotate this file.
# If something goes wrong, that's okay -- we'll gracefully revert
# to a plain markup display.
if is_annotate:
# Try to annotate this file, but don't croak if we fail.
try:
blame_source, revision = request.repos.annotate(path, rev)
annotation = 'annotated'
blame_source, revision = request.repos.annotate(path, rev, False)
if check_freshness(request, None, revision, weak=1):
return
# Create BLAME_DATA list from BLAME_SOURCE, adding diff_href
# items to each relevant "line".
blame_data = []
for item in blame_source:
item.diff_href = None
if item.prev_rev:
item.diff_href = request.get_url(view_func=view_diff,
params={'r1': item.prev_rev,
'r2': item.rev},
escape=1, partial=1)
blame_data.append(item)
annotation = 'annotated'
except vclib.NonTextualFileContents:
annotation = 'binary'
except:
annotation = 'error'
# Grab the file contents.
fp, revision = request.repos.openfile(path, rev, {'cvs_oldkeywords' : 1})
if check_freshness(request, None, revision, weak=1):
fp.close()
return
lines = markup_stream_pygments(request, cfg, blame_source, fp,
path[-1], mime_type, encoding)
file_lines = fp.readlines()
fp.close()
data = common_template_data(request, revision)
# Do we have a differing number of file content lines and
# annotation items? That's no good. Call it an error and don't
# bother attempting the annotation display.
if blame_data and (len(file_lines) != len(blame_data)):
annotation = 'error'
blame_data = None
# Try to markup the file contents/annotation. If we get an error
# and we were colorizing the stream, try once more without the
# colorization enabled.
colorize = cfg.options.enable_syntax_coloration
try:
lines = markup_stream(request, cfg, blame_data, file_lines,
path[-1], mime_type, encoding, colorize)
except:
if colorize:
lines = markup_stream(request, cfg, blame_data, file_lines,
path[-1], mime_type, encoding, False)
else:
raise debug.ViewVCException('Error displaying file contents',
'500 Internal Server Error')
data = common_template_data(request, revision, mime_type)
data.merge(ezt.TemplateData({
'mime_type' : mime_type,
'log' : None,
@@ -1814,10 +1929,12 @@ def markup_or_annotate(request, is_annotate):
revs = request.repos.itemlog(path, revision, vclib.SORTBY_REV,
0, 1, options)
entry = revs[-1]
lf = LogFormatter(request, entry.log)
data['date'] = make_time_string(entry.date, cfg)
data['author'] = entry.author
data['changed'] = entry.changed
data['log'] = format_log(request, entry.log)
data['log'] = lf.get(maxlen=0, htmlize=1)
data['size'] = entry.size
if entry.date is not None:
@@ -2053,9 +2170,11 @@ def view_directory(request):
row.date = make_time_string(file.date, cfg)
row.ago = html_time(request, file.date)
if cfg.options.show_logs:
row.log = format_log(request, file.log)
row.short_log = format_log(request, file.log,
maxlen=cfg.options.short_log_len)
debug.t_start("dirview_logformat")
lf = LogFormatter(request, file.log)
row.log = lf.get(maxlen=0, htmlize=1)
row.short_log = lf.get(maxlen=cfg.options.short_log_len, htmlize=1)
debug.t_end("dirview_logformat")
row.lockinfo = file.lockinfo
row.anchor = request.server.escape(file.name)
row.name = request.server.escape(file.name)
@@ -2432,7 +2551,6 @@ def view_log(request):
entry.ago = None
if rev.date is not None:
entry.ago = html_time(request, rev.date, 1)
entry.log = format_log(request, rev.log or '')
entry.size = rev.size
entry.lockinfo = rev.lockinfo
entry.branch_point = None
@@ -2440,6 +2558,9 @@ def view_log(request):
entry.orig_path = None
entry.copy_path = None
lf = LogFormatter(request, rev.log or '')
entry.log = lf.get(maxlen=0, htmlize=1)
entry.view_href = None
entry.download_href = None
entry.download_text_href = None
@@ -3288,7 +3409,8 @@ def view_diff(request):
fvi = get_file_view_info(request, path_left, rev1)
left = _item(date=make_time_string(log_entry1.date, cfg),
author=log_entry1.author,
log=format_log(request, log_entry1.log),
log=LogFormatter(request,
log_entry1.log).get(maxlen=0, htmlize=1),
size=log_entry1.size,
ago=ago1,
path=path_left,
@@ -3304,7 +3426,8 @@ def view_diff(request):
fvi = get_file_view_info(request, path_right, rev2)
right = _item(date=make_time_string(log_entry2.date, cfg),
author=log_entry2.author,
log=format_log(request, log_entry2.log),
log=LogFormatter(request,
log_entry2.log).get(maxlen=0, htmlize=1),
size=log_entry2.size,
ago=ago2,
path=path_right,
@@ -3552,7 +3675,8 @@ def view_revision(request):
propnames.sort()
props = []
for name in propnames:
value = format_log(request, revprops[name])
lf = LogFormatter(request, revprops[name])
value = lf.get(maxlen=0, htmlize=1)
undisplayable = ezt.boolean(0)
# skip non-utf8 property names
try:
@@ -3675,13 +3799,14 @@ def view_revision(request):
escape=1)
jump_rev_action, jump_rev_hidden_values = \
request.get_form(params={'revision': None})
lf = LogFormatter(request, msg)
data = common_template_data(request)
data.merge(ezt.TemplateData({
'rev' : str(rev),
'author' : author,
'date' : date_str,
'log' : format_log(request, msg),
'log' : lf.get(maxlen=0, htmlize=1),
'properties' : props,
'ago' : date is not None and html_time(request, date, 1) or None,
'changes' : changes,
@@ -4039,9 +4164,10 @@ def build_commit(request, files, max_files, dir_strip, format):
commit.log = None
commit.short_log = None
else:
commit.log = format_log(request, desc, 0, format != 'rss')
commit.short_log = format_log(request, desc, cfg.options.short_log_len,
format != 'rss')
lf = LogFormatter(request, desc)
htmlize = (format != 'rss')
commit.log = lf.get(maxlen=0, htmlize=htmlize)
commit.short_log = lf.get(maxlen=cfg.options.short_log_len, htmlize=htmlize)
commit.author = request.server.escape(author)
commit.rss_date = make_rss_time_string(date, request.cfg)
if request.roottype == 'svn':
@@ -4335,8 +4461,9 @@ def list_roots(request):
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)
lf = LogFormatter(request, msg)
log = lf.get(maxlen=0, htmlize=1)
short_log = lf.get(maxlen=cfg.options.short_log_len, htmlize=1)
lastmod = _item(ago=ago, author=author, date=date_str, log=log,
short_log=short_log, rev=str(youngest_rev))
except:

View File

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

View File

@@ -92,13 +92,15 @@ numbers, and not literal):
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
new release into trunk's CHANGES file.
15. Update the Tigris.org website (^/trunk/www/index.html) to refer to
the new release files and commit.
16. Edit the file 'lib/viewvc.py' again, incrementing the patch number
assigned to the __version__ variable. Add a new empty block in
the branch's CHANGES file. Commit your changes:
svn ci -m "Bump latest advertised release."
16. Back on the release branch, edit the file 'lib/viewvc.py' again,
incrementing the patch number assigned to the __version__
variable. Add a new empty block in the branch's CHANGES file.
Commit your changes:
svn ci -m "Begin a new release cycle."
@@ -113,3 +115,6 @@ numbers, and not literal):
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).
19. Merge CHANGES for this release into the CHANGES file for newer
release lines and commit.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- Mode: python -*-
#
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
# Copyright (C) 1999-2012 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