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

Compare commits

..

29 Commits

Author SHA1 Message Date
cmpilato
739c51dd7c Tag the 1.1.17 final release.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/tags/1.1.17@2810 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-25 13:58:00 +00:00
cmpilato
167a2a041b Merge from trunk r2808, whose log message read like so:
Fix issue #516 ("Regression: UnboundLocalError: local variable
   'log_pagestart' referenced before assignment").
   
   * lib/viewvc.py
     (view_log): Initialize the 'log_pagestart' variable.

Also:

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

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

Also:

* CHANGES
  Note this change.

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

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2795 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 14:40:32 +00:00
cmpilato
7feecdd512 Merge from trunk r2791 and r2792, which did the following:
Fix issue #515 ("XSS bug in diff view (CVE-2012-4533)").

   * lib/viewvc.py
     (DiffSource._get_row): Pass the "extra" line information through the
       formatter code so that, at a minimum, it's HTML-escaped.

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

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

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2793 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-24 13:29:49 +00:00
cmpilato
a087c06a88 Merge from trunk r2788, whose log message read like so:
Fix issue #512 ("'Select for diffs' does not work across pages").
   
   * lib/viewvc.py
     (view_log): Preserve the 'log_pagestart' query value when generating
       the 'select for diff' links so that clicking the link returns you to
       the same page (modulo repagination due to new commits in the race
       window ... but let's not think about that).  Also, preserve the
       'r1' query parameter when generating the paging form.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2789 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-10-03 14:24:49 +00:00
cmpilato
acce6a556d Merge from trunk r2784, whose log message read like so:
Finish issue #510 ("Block the expensive display of binary files").

   Add a new configuration option 'binary_mime_types' which accepts a
   comma-delimited list of MIME content type patterns ('text/plain', or
   'image/*', etc.) against which versioned file MIME types are
   compared for the purposes of deciding whether to allow their display
   in the 'markup', 'annotate', 'diff' and 'patch' views.
   
   * conf/viewvc.conf.dist
     (binary_mime_types): Describe new option.
   
   * lib/config.py
     (_force_multi_value): Add 'binary_file_types' to the list of
       multi-value options.
     (Config.set_defaults): Initialize cfg.options.binary_mime_types.
   
   * lib/viewvc.py
     (is_binary_file_mime_type): New function.
     (get_file_view_info): Use is_binary_file_mime_type() to determine
       whether to return links to content-ful views of the input file.
     (markup_or_annotate, view_diff): Use is_binary_file_mime_type() to
       deny display of so-deemed binary files.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2785 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-09-05 14:55:05 +00:00
cmpilato
4670019d3a Merge from trunk r2781.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2782 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-22 20:23:27 +00:00
cmpilato
9ce7372e1a Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2778 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-22 18:53:40 +00:00
cmpilato
e1959ac2e5 Rolling 1.1.15 today.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2775 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-22 18:41:47 +00:00
cmpilato
cb38ccc929 Merge from trunk r2770 (more ra_svn improvements).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2773 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-19 18:50:01 +00:00
cmpilato
f2b82132c2 Merge from trunk r2771, whose log message read like so:
* lib/viewvc.py
     (LogFormatter.get): Fix a regression introduced in 1.1.14's handling
       of log messages when not HTML-ifying them (for example, when serving
       them up via RSS).
   
   Patch by: Christoph Sommer <christoph.sommer{__AT__}uibk.ac.at>

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2772 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-19 18:48:01 +00:00
cmpilato
5caf3a4437 Merge r2767 and r2768 from trunk (bugfixes to recent commits).
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2769 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 01:40:03 +00:00
cmpilato
06ee4df42d Merge from trunk r2765, whose log message read like so:
Optimize revision info fetches for remote SVN repositories to avoid
   unnecessary (and somewhat expensive) work.
   
   * lib/vclib/svn/svn_ra.py
     (client_log): Add 'include_changes' parameter, pass to the
       Subversion log APIs.  Callers updated.
     (_revinfo): Was _revinfo_raw().  Add 'include_changed_paths'
       parameter.  Now handles the revinfo cache, only fetches changed
       paths when required, and bails out early of authz checks when
       possible.  All internal callers of revinfo() have been updated to
       use this interface instead.

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2766 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 01:15:50 +00:00
cmpilato
0a6b13145e Merge from trunk r2763, which did somethin' a little bit like this:
Fix a couple of correctness/performance regressions in the remote SVN
   annotate view recently introduced.
   
   * lib/vclib/svn/svn_ra.py
     (RemoteSubversionRepository.annotate): Pass 'svn_cross_copies'
       option to itemlog() so that annotation history isn't unnaturally
       truncated.
     (RemoteSubversionRepository._blame_cb): Only consult the revinfo
       cache when authz checks are required.  This improves the speed of
       the operation when universal read access is granted to the user.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2764 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 00:52:29 +00:00
cmpilato
65d3568c92 Merge from trunk r2758, whose log message read like so:
Fix a security issue: When a readable path is copied from an
   unreadable one, Subversion will obscure the fact that the operation
   was a copy (by removing copyfrom info) and will deem the log message
   for the revision in which the copy occurred to be unreadable.  ViewVC
   was only doing the former bit; now it does the latter, too.
   
   * lib/vclib/svn/svn_repos.py
     (LocalSubversionRepository._get_changed_paths): Set found_unreadable
       when we have to hide a copyfrom path, too.

Also:

* CHANGES:
  Note this change.

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

Also:

* CHANGES: Note the changes this offers.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2761 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-16 00:03:57 +00:00
cmpilato
eb6b575701 Merge from trunk r2753, which contains some release process doc tweaks.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2754 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 13:06:43 +00:00
cmpilato
8d82b6f0d6 Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2751 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 12:55:19 +00:00
cmpilato
5a51470cbc Let's try to release 1.1.14 today.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2748 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-06-12 12:44:11 +00:00
cmpilato
acc0783468 Merge from trunk r2746, whose log message reads like so:
Fix issue #246 ("Add support for issue tracker links in commit-log
   viewer").
   
   NOTE: There are some limitations here, most prominantly that commas
   can't be used in the regular expressions which define replacements,
   and that only match groupings 0-9 can be used in the replacement
   format string.
   
   * conf/viewvc.conf.dist
     (custom_log_formatting): New configuration option.
   
   * lib/config.py
     (Config._force_multi_value, Config.set_defaults): Add handling of
       new 'custom_log_formatting' option.
   
   * lib/viewvc.py
     (ViewVCHtmlFormatter.format_custom_url): New formatter callback.
     (LogFormatter.get): Register the new formatter callback for rules
       found in the 'custom_log_formatting' option value.

Also:

* CHANGES:
  Note this change.


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

Also:

* CHANGES
  Note this change.


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

Also:

* CHANGES
  Note this change.

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

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

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

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

Also:

* CHANGES
  Note this change.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2734 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-02-01 19:49:22 +00:00
cmpilato
6f3d9a3a00 Merge some notes tweaks from ^/trunk:r2729.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2732 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-02-01 19:27:51 +00:00
cmpilato
1d7307c09b Begin a new release cycle.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/branches/1.1.x@2728 8cb11bc2-c004-0410-86c3-e597b4017df7
2012-01-23 18:53:09 +00:00
10 changed files with 564 additions and 182 deletions

29
CHANGES
View File

@@ -1,3 +1,31 @@
Version 1.1.17 (released 25-Oct-2012)
* fix exception caused by uninitialized variable usage (issue #516)
Version 1.1.16 (released 24-Oct-2012)
* security fix: escape "extra" diff info to avoid XSS attack (issue #515)
* add 'binary_mime_types' configuration option and handling (issue #510)
* fix 'select for diffs' persistence across log pages (issue #512)
* remove lock status and filesize check on directories in remote SVN views
* fix bogus 'Annotation of' page title for non-annotated view (issue #514)
Version 1.1.15 (released 22-Jun-2012)
* security fix: complete authz support for remote SVN views (issue #353)
* security fix: log msg leak in SVN revision view with unreadable copy source
* fix several instances of incorrect information in remote SVN views
* increase performance of some revision metadata lookups in remote SVN views
* fix RSS feed regression introduced in 1.1.14
Version 1.1.14 (released 12-Jun-2012)
* fix annotation of svn files with non-URI-safe paths (issue #504)
* handle file:/// Subversion rootpaths as local roots (issue #446)
* fix bug caused by trying to case-normalize anon usernames (issue #505)
* speed up log handling by reusing tokenization results (issue #506)
* add support for custom 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)
@@ -38,7 +66,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

@@ -391,6 +391,24 @@
##
#allowed_views = annotate, diff, markup, roots
## Comma-delimited list of MIME content types (with support for fnmatch-
## style glob characters) which are considered not-human-readable and for
## which ViewVC will neither generate links to, nor support the direct
## display of, non-checkout views which carry the file's content (the
## 'markup', 'annotate', 'diff', and 'patch' views).
##
## NOTE: Handling of this option is given priority over ViewVC's
## longstanding support for showing web-friendly file formats -- even
## binary ones such as "image/jpeg" and "image/gif" -- in the 'markup'
## view. Thus, if you add "image/*" to this list, 'markup'-view
## display of JPEG, GIF, and PNG images will be disabled.
##
## Example:
## binary_mime_types = application/octet-stream, image/*, application/pdf,
## application/vnd*, application/msword, audio/*
#
#binary_mime_types =
## authorizer: The name of the ViewVC authorizer plugin to use when
## authorizing access to repository contents. This value must be the
## name of a Python module addressable as vcauth.MODULENAME (most
@@ -433,6 +451,26 @@
##
#mangle_email_addresses = 0
## custom_log_formatting: Specifies mappings of regular expressions to
## substitution format strings used to URL-ize strings found in
## revision log messages. Multiple mappings (specified as
## REGEXP:FORMATSTRING) may be defined, separated by commas.
##
## NOTE: Due to a limitation of the configuration format, commas may
## not be used in the regular expression portion of each mapping.
## Commas "in the raw" may not be used in the format string portion,
## either, but you can probably use the URI-encoded form of the comma
## ("%2C") instead with no ill side-effects. If you must specify a
## colon character in either the regular expression or the format
## string, escape it with a preceding backslash ("\:").
##
## Example:
## custom_log_formatting =
## artf[0-9]+ : http://example.com/tracker?id=\0,
## issue ([0-9]+) : http://example.com/bug?id=\1&opts=full%2csortby=id
##
#custom_log_formatting =
## default_file_view: "log", "co", or "markup"
## Controls whether the default view for file URLs is a checkout view or
## a log view. "log" is the default for backwards compatibility with old
@@ -541,7 +579,7 @@
## (Only works well for C source files, otherwise diff's heuristic falls short.)
## ('-p' option to diff)
##
#hr_funout = 0
#hr_funout = 1
## hr_ignore_white: Ignore whitespace (indendation and stuff) for human
## readable diffs.

View File

@@ -112,6 +112,8 @@ class Config:
_force_multi_value = (
# Configuration values with multiple, comma-separated values.
'allowed_views',
'binary_mime_types',
'custom_log_formatting',
'cvs_roots',
'kv_files',
'languages',
@@ -398,7 +400,9 @@ class Config:
self.options.allowed_views = ['annotate', 'diff', 'markup', 'roots']
self.options.authorizer = None
self.options.mangle_email_addresses = 0
self.options.custom_log_formatting = []
self.options.default_file_view = "log"
self.options.binary_mime_types = []
self.options.http_expiration_time = 600
self.options.generate_etags = 1
self.options.svn_ignore_mimetype = 0

View File

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

@@ -19,27 +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 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 os.path.normpath(rootpath)
return rootpath
def expand_root_parent(parent_path):

View File

@@ -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
@@ -51,12 +54,14 @@ def get_directory_props(ra_session, path, rev):
props = ra.svn_ra_get_dir(ra_session, path, rev)
return props
def client_log(url, start_rev, end_rev, log_limit, cross_copies,
cb_func, ctx):
def client_log(url, start_rev, end_rev, log_limit, include_changes,
cross_copies, cb_func, ctx):
include_changes = include_changes and 1 or 0
cross_copies = cross_copies and 1 or 0
try:
client.svn_client_log4([url], start_rev, start_rev, end_rev,
log_limit, 1, not cross_copies, 0, None,
cb_func, ctx)
log_limit, include_changes, not cross_copies,
0, None, cb_func, ctx)
except AttributeError:
# Wrap old svn_log_message_receiver_t interface with a
# svn_log_entry_t one.
@@ -72,15 +77,14 @@ def client_log(url, start_rev, end_rev, log_limit, cross_copies,
}
cb_func(log_entry, pool)
client.svn_client_log2([url], start_rev, end_rev, log_limit,
1, not cross_copies, cb_convert, ctx)
include_changes, not cross_copies, cb_convert, ctx)
### END COMPATABILITY CODE ###
class LogCollector:
### TODO: Make this thing authz-aware
def __init__(self, path, show_all_logs, lockinfo):
def __init__(self, path, show_all_logs, lockinfo, access_check_func):
# This class uses leading slashes for paths internally
if not path:
self.path = '/'
@@ -89,8 +93,12 @@ class LogCollector:
self.logs = []
self.show_all_logs = show_all_logs
self.lockinfo = lockinfo
self.access_check_func = access_check_func
self.done = False
def add_log(self, log_entry, pool):
if self.done:
return
paths = log_entry.changed_paths
revision = log_entry.revision
msg, author, date, revprops = _split_revprops(log_entry.revprops)
@@ -114,9 +122,13 @@ class LogCollector:
if change.copyfrom_path:
this_path = change.copyfrom_path + self.path[len(changed_path):]
if self.show_all_logs or this_path:
entry = Revision(revision, date, author, msg, None, self.lockinfo,
self.path[1:], None, None)
self.logs.append(entry)
if self.access_check_func is None \
or self.access_check_func(self.path[1:], revision):
entry = Revision(revision, date, author, msg, None, self.lockinfo,
self.path[1:], None, None)
self.logs.append(entry)
else:
self.done = True
if this_path:
self.path = this_path
@@ -252,14 +264,15 @@ class RemoteSubversionRepository(vclib.Repository):
### rev here should be the last history revision of the URL
client.svn_client_cat(core.Stream(stream), url, _rev2optrev(rev), self.ctx)
core.svn_stream_close(stream)
return SelfCleanFP(tmp_file), self._get_last_history_rev(path_parts, rev)
lh_rev, c_rev = self._get_last_history_rev(path_parts, rev)
return SelfCleanFP(tmp_file), lh_rev
def listdir(self, path_parts, rev, options):
path = self._getpath(path_parts)
if self.itemtype(path_parts, rev) != vclib.DIR: # does auth-check
raise vclib.Error("Path '%s' is not a directory." % path)
rev = self._getrev(rev)
entries = [ ]
entries = []
dirents, locks = self._get_dirents(path, rev)
for name in dirents.keys():
entry = dirents[name]
@@ -267,8 +280,9 @@ class RemoteSubversionRepository(vclib.Repository):
kind = vclib.DIR
elif entry.kind == core.svn_node_file:
kind = vclib.FILE
if vclib.check_path_access(self, path_parts + [name], kind, rev):
entries.append(vclib.DirEntry(name, kind))
else:
kind = None
entries.append(vclib.DirEntry(name, kind))
return entries
def dirlogs(self, path_parts, rev, entries, options):
@@ -279,11 +293,13 @@ class RemoteSubversionRepository(vclib.Repository):
dirents, locks = self._get_dirents(path, rev)
for entry in entries:
entry_path_parts = path_parts + [entry.name]
if not vclib.check_path_access(self, entry_path_parts, entry.kind, rev):
dirent = dirents.get(entry.name, None)
# dirents is authz-sanitized, so ensure the entry is found therein.
if dirent is None:
continue
dirent = dirents[entry.name]
# Get authz-sanitized revision metadata.
entry.date, entry.author, entry.log, revprops, changes = \
self.revinfo(dirent.created_rev)
self._revinfo(dirent.created_rev)
entry.rev = str(dirent.created_rev)
entry.size = dirent.size
entry.lockinfo = None
@@ -297,28 +313,51 @@ class RemoteSubversionRepository(vclib.Repository):
rev = self._getrev(rev)
url = self._geturl(path)
# Use ls3 to fetch the lock status for this item.
lockinfo = None
basename = path_parts and path_parts[-1] or ""
dirents, locks = list_directory(url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
if locks.has_key(basename):
lockinfo = locks[basename].owner
# If this is a file, fetch the lock status and size (as of REV)
# for this item.
lockinfo = size_in_rev = None
if path_type == vclib.FILE:
basename = path_parts[-1]
list_url = self._geturl(self._getpath(path_parts[:-1]))
dirents, locks = list_directory(list_url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
if locks.has_key(basename):
lockinfo = locks[basename].owner
if dirents.has_key(basename):
size_in_rev = dirents[basename].size
# Special handling for the 'svn_latest_log' scenario.
### FIXME: Don't like this hack. We should just introduce
### something more direct in the vclib API.
if options.get('svn_latest_log', 0):
dir_lh_rev, dir_c_rev = self._get_last_history_rev(path_parts, rev)
date, author, log, revprops, changes = self._revinfo(dir_lh_rev)
return [vclib.Revision(dir_lh_rev, str(dir_lh_rev), date, author,
None, log, size_in_rev, lockinfo)]
def _access_checker(check_path, check_rev):
return vclib.check_path_access(self, _path_parts(check_path),
path_type, check_rev)
# It's okay if we're told to not show all logs on a file -- all
# the revisions should match correctly anyway.
lc = LogCollector(path, options.get('svn_show_all_dir_logs', 0), lockinfo)
lc = LogCollector(path, options.get('svn_show_all_dir_logs', 0),
lockinfo, _access_checker)
cross_copies = options.get('svn_cross_copies', 0)
log_limit = 0
if limit:
log_limit = first + limit
client_log(url, _rev2optrev(rev), _rev2optrev(1), log_limit,
client_log(url, _rev2optrev(rev), _rev2optrev(1), log_limit, 1,
cross_copies, lc.add_log, self.ctx)
revs = lc.logs
revs.sort()
prev = None
for rev in revs:
# Swap out revision info with stuff from the cache (which is
# authz-sanitized).
rev.date, rev.author, rev.log, revprops, changes \
= self._revinfo(rev.number)
rev.prev = prev
prev = rev
revs.reverse()
@@ -345,6 +384,18 @@ class RemoteSubversionRepository(vclib.Repository):
rev = self._getrev(rev)
url = self._geturl(path)
# Examine logs for the file to determine the oldest revision we are
# permitted to see.
log_options = {
'svn_cross_copies' : 1,
'svn_show_all_dir_logs' : 1,
}
revs = self.itemlog(path_parts, rev, vclib.SORTBY_REV, 0, 0, log_options)
oldest_rev = revs[-1].number
# Now calculate the annotation data. Note that we'll not
# inherently trust the provided author and date, because authz
# rules might necessitate that we strip that information out.
blame_data = []
def _blame_cb(line_no, revision, author, date,
@@ -352,22 +403,27 @@ class RemoteSubversionRepository(vclib.Repository):
prev_rev = None
if revision > 1:
prev_rev = revision - 1
# If we have an invalid revision, clear the date and author
# values. Otherwise, if we have authz filtering to do, use the
# revinfo cache to do so.
if revision < 0:
date = author = None
elif self.auth:
date, author, msg, revprops, changes = self._revinfo(revision)
# Strip text if the caller doesn't want it.
if not include_text:
line = None
blame_data.append(vclib.Annotation(line, line_no + 1, revision, prev_rev,
author, None))
author, date))
client.svn_client_blame(url, _rev2optrev(1), _rev2optrev(rev),
_blame_cb, self.ctx)
client.blame2(url, _rev2optrev(rev), _rev2optrev(oldest_rev),
_rev2optrev(rev), _blame_cb, self.ctx)
return blame_data, rev
def revinfo(self, rev):
rev = self._getrev(rev)
cached_info = self._revinfo_cache.get(rev)
if not cached_info:
cached_info = self._revinfo_raw(rev)
self._revinfo_cache[rev] = cached_info
return tuple(cached_info)
return self._revinfo(rev, 1)
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
p1 = self._getpath(path_parts1)
@@ -382,7 +438,7 @@ class RemoteSubversionRepository(vclib.Repository):
args = vclib._diff_args(type, options)
def _date_from_rev(rev):
date, author, msg, revprops, changes = self.revinfo(rev)
date, author, msg, revprops, changes = self._revinfo(rev)
return date
try:
@@ -421,49 +477,84 @@ class RemoteSubversionRepository(vclib.Repository):
def _geturl(self, path=None):
if not path:
return self.rootpath
return self.rootpath + '/' + urllib.quote(path, "/*~")
path = self.rootpath + '/' + urllib.quote(path)
return _canonicalize_path(path)
def _get_dirents(self, path, rev):
"""Return a 2-type of dirents and locks, possibly reading/writing
from a local cache of that information."""
from a local cache of that information. This functions performs
authz checks, stripping out unreadable dirents."""
dir_url = self._geturl(path)
path_parts = _path_parts(path)
if path:
key = str(rev) + '/' + path
else:
key = str(rev)
# Ensure that the cache gets filled...
dirents_locks = self._dirent_cache.get(key)
if not dirents_locks:
dirents, locks = list_directory(dir_url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
tmp_dirents, locks = list_directory(dir_url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
dirents = {}
for name, dirent in tmp_dirents.items():
dirent_parts = path_parts + [name]
kind = dirent.kind
if (kind == core.svn_node_dir or kind == core.svn_node_file) \
and vclib.check_path_access(self, dirent_parts,
kind == core.svn_node_dir \
and vclib.DIR or vclib.FILE, rev):
lh_rev, c_rev = self._get_last_history_rev(dirent_parts, rev)
dirent.created_rev = lh_rev
dirents[name] = dirent
dirents_locks = [dirents, locks]
self._dirent_cache[key] = dirents_locks
# ...then return the goodies from the cache.
return dirents_locks[0], dirents_locks[1]
def _get_last_history_rev(self, path_parts, rev):
"""Return the a 2-tuple which contains:
- the last interesting revision equal to or older than REV in
the history of PATH_PARTS.
- the created_rev of of PATH_PARTS as of REV."""
path = self._getpath(path_parts)
url = self._geturl(self._getpath(path_parts))
optrev = _rev2optrev(rev)
# Get the last-changed-rev.
revisions = []
def _info_cb(path, info, pool, retval=revisions):
revisions.append(info.last_changed_rev)
client.svn_client_info(url, optrev, optrev, _info_cb, 0, self.ctx)
return revisions[0]
def _revinfo_raw(self, rev):
# return 5-tuple (date, author, msg, revprops, changes)
optrev = _rev2optrev(rev)
revs = []
last_changed_rev = revisions[0]
# Now, this object might not have been directly edited since the
# last-changed-rev, but it might have been the child of a copy.
# To determine this, we'll run a potentially no-op log between
# LAST_CHANGED_REV and REV.
lc = LogCollector(path, 1, None, None)
client_log(url, optrev, _rev2optrev(last_changed_rev), 1, 1, 0,
lc.add_log, self.ctx)
revs = lc.logs
if revs:
revs.sort()
return revs[0].number, last_changed_rev
else:
return last_changed_rev, last_changed_rev
def _revinfo_fetch(self, rev, include_changed_paths=0):
need_changes = include_changed_paths or self.auth
revs = []
def _log_cb(log_entry, pool, retval=revs):
### Subversion 1.5 and earlier didn't offer the 'changed_paths2'
### hash, and in Subversion 1.6, it's offered but broken.
try:
changed_paths = log_entry.changed_paths2
paths = (changed_paths or {}).keys()
except:
changed_paths = log_entry.changed_paths
paths = (changed_paths or {}).keys()
paths.sort(lambda a, b: _compare_paths(a, b))
# If Subversion happens to call us more than once, we choose not
# to care.
if retval:
return
revision = log_entry.revision
msg, author, date, revprops = _split_revprops(log_entry.revprops)
action_map = { 'D' : vclib.DELETED,
@@ -471,22 +562,42 @@ class RemoteSubversionRepository(vclib.Repository):
'R' : vclib.REPLACED,
'M' : vclib.MODIFIED,
}
# Easy out: if we won't use the changed-path info, just return a
# changes-less tuple.
if not need_changes:
return revs.append([date, author, msg, revprops, None])
# Subversion 1.5 and earlier didn't offer the 'changed_paths2'
# hash, and in Subversion 1.6, it's offered but broken.
try:
changed_paths = log_entry.changed_paths2
paths = (changed_paths or {}).keys()
except:
changed_paths = log_entry.changed_paths
paths = (changed_paths or {}).keys()
paths.sort(lambda a, b: _compare_paths(a, b))
# If we get this far, our caller needs changed-paths, or we need
# them for authz-related sanitization.
changes = []
found_readable = found_unreadable = 0
for path in paths:
change = changed_paths[path]
### svn_log_changed_path_t (which we might get instead of the
### svn_log_changed_path2_t we'd prefer) doesn't have the
### 'node_kind' member.
# svn_log_changed_path_t (which we might get instead of the
# svn_log_changed_path2_t we'd prefer) doesn't have the
# 'node_kind' member.
pathtype = None
if hasattr(change, 'node_kind'):
if change.node_kind == core.svn_node_dir:
pathtype = vclib.DIR
elif change.node_kind == core.svn_node_file:
pathtype = vclib.FILE
### svn_log_changed_path2_t only has the 'text_modified' and
### 'props_modified' bits in Subversion 1.7 and beyond. And
### svn_log_changed_path_t is without.
# svn_log_changed_path2_t only has the 'text_modified' and
# 'props_modified' bits in Subversion 1.7 and beyond. And
# svn_log_changed_path_t is without.
text_modified = props_modified = 0
if hasattr(change, 'text_modified'):
if change.text_modified == core.svn_tristate_true:
@@ -494,9 +605,10 @@ class RemoteSubversionRepository(vclib.Repository):
if hasattr(change, 'props_modified'):
if change.props_modified == core.svn_tristate_true:
props_modified = 1
### Wrong, diddily wrong wrong wrong. Can you say,
### "Manufacturing data left and right because it hurts to
### figure out the right stuff?"
# Wrong, diddily wrong wrong wrong. Can you say,
# "Manufacturing data left and right because it hurts to
# figure out the right stuff?"
action = action_map.get(change.action, vclib.MODIFIED)
if change.copyfrom_path and change.copyfrom_rev:
is_copy = 1
@@ -510,15 +622,16 @@ class RemoteSubversionRepository(vclib.Repository):
base_path = path
base_rev = revision - 1
### Check authz rules (we lie about the path type)
# Check authz rules (sadly, we have to lie about the path type)
parts = _path_parts(path)
if vclib.check_path_access(self, parts, vclib.FILE, revision):
if is_copy and base_path and (base_path != path):
parts = _path_parts(base_path)
if vclib.check_path_access(self, parts, vclib.FILE, base_rev):
if not vclib.check_path_access(self, parts, vclib.FILE, base_rev):
is_copy = 0
base_path = None
base_rev = None
found_unreadable = 1
changes.append(SVNChangedPath(path, revision, pathtype, base_path,
base_rev, action, is_copy,
text_modified, props_modified))
@@ -526,16 +639,45 @@ class RemoteSubversionRepository(vclib.Repository):
else:
found_unreadable = 1
# If our caller doesn't want changed-path stuff, and we have
# the info we need to make an authz determination already,
# quit this loop and get on with it.
if (not include_changed_paths) and found_unreadable and found_readable:
break
# Filter unreadable information.
if found_unreadable:
msg = None
if not found_readable:
author = None
date = None
revs.append([date, author, msg, revprops, changes])
client_log(self.rootpath, optrev, optrev, 1, 0, _log_cb, self.ctx)
# Drop unrequested changes.
if not include_changed_paths:
changes = None
# Add this revision information to the "return" array.
retval.append([date, author, msg, revprops, changes])
optrev = _rev2optrev(rev)
client_log(self.rootpath, optrev, optrev, 1, need_changes, 0,
_log_cb, self.ctx)
return tuple(revs[0])
def _revinfo(self, rev, include_changed_paths=0):
"""Internal-use, cache-friendly revision information harvester."""
# Consult the revinfo cache first. If we don't have cached info,
# or our caller wants changed paths and we don't have those for
# this revision, go do the real work.
rev = self._getrev(rev)
cached_info = self._revinfo_cache.get(rev)
if not cached_info \
or (include_changed_paths and cached_info[4] is None):
cached_info = self._revinfo_fetch(rev, include_changed_paths)
self._revinfo_cache[rev] = cached_info
return cached_info
##--- custom --##
def get_youngest_revision(self):
@@ -553,23 +695,16 @@ class RemoteSubversionRepository(vclib.Repository):
old_path = results[old_rev]
except KeyError:
raise vclib.ItemNotFound(path)
return _cleanup_path(old_path)
old_path = _cleanup_path(old_path)
old_path_parts = _path_parts(old_path)
# Check access (lying about path types)
if not vclib.check_path_access(self, old_path_parts, vclib.FILE, old_rev):
raise vclib.ItemNotFound(path)
return old_path
def created_rev(self, path, rev):
# NOTE: We can't use svn_client_propget here because the
# interfaces in that layer strip out the properties not meant for
# human consumption (such as svn:entry:committed-rev, which we are
# using here to get the created revision of PATH@REV).
kind = ra.svn_ra_check_path(self.ra_session, path, rev)
if kind == core.svn_node_none:
raise vclib.ItemNotFound(_path_parts(path))
elif kind == core.svn_node_dir:
props = get_directory_props(self.ra_session, path, rev)
elif kind == core.svn_node_file:
fetched_rev, props = ra.svn_ra_get_file(self.ra_session, path, rev, None)
return int(props.get(core.SVN_PROP_ENTRY_COMMITTED_REV,
SVN_INVALID_REVNUM))
lh_rev, c_rev = self._get_last_history_rev(_path_parts(path), rev)
return lh_rev
def last_rev(self, path, peg_revision, limit_revision=None):
"""Given PATH, known to exist in PEG_REVISION, find the youngest

View File

@@ -43,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
@@ -115,9 +122,10 @@ def _rootpath2url(rootpath, path):
rootpath = urllib.quote(rootpath)
path = urllib.quote(path)
if drive:
return 'file:///' + drive + rootpath + '/' + path
url = 'file:///' + drive + rootpath + '/' + path
else:
return 'file://' + rootpath + '/' + path
url = 'file://' + rootpath + '/' + path
return _canonicalize_path(url)
# Given a dictionary REVPROPS of revision properties, pull special
@@ -643,6 +651,7 @@ class LocalSubversionRepository(vclib.Repository):
is_copy = 0
change.base_path = None
change.base_rev = None
found_unreadable = 1
changedpaths[path] = SVNChangedPath(path, rev, pathtype,
change.base_path,
change.base_rev, action,

View File

@@ -14,7 +14,7 @@
#
# -----------------------------------------------------------------------
__version__ = '1.1.13'
__version__ = '1.1.17'
# this comes from our library; measure the startup time
import debug
@@ -24,6 +24,7 @@ debug.t_start('imports')
# standard modules that we know are in the path or builtin
import sys
import os
import fnmatch
import gzip
import mimetypes
import re
@@ -1017,6 +1018,15 @@ def default_view(mime_type, cfg):
return view_markup
return view_checkout
def is_binary_file_mime_type(mime_type, cfg):
"""Return True iff MIME_TYPE is set and matches one of the binary
file mime type patterns in CFG."""
if mime_type:
for pattern in cfg.options.binary_mime_types:
if fnmatch.fnmatch(mime_type, pattern):
return True
return False
def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1):
"""Return an object holding common hrefs and a viewability flag used
for various views of FILENAME at revision REV whose MIME type is
@@ -1077,7 +1087,12 @@ def get_file_view_info(request, where, rev=None, mime_type=None, pathrev=-1):
params={'revision': rev},
escape=1)
prefer_markup = default_view(mime_type, request.cfg) == view_markup
is_binary_file = is_binary_file_mime_type(mime_type, request.cfg)
if is_binary_file:
download_text_href = annotate_href = view_href = None
prefer_markup = False
else:
prefer_markup = default_view(mime_type, request.cfg) == view_markup
return _item(view_href=view_href,
download_href=download_href,
@@ -1099,6 +1114,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 +1222,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 +1278,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 +1332,80 @@ 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.
log = self.log
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',
@@ -1693,7 +1793,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:
@@ -1745,6 +1846,11 @@ def markup_or_annotate(request, is_annotate):
revision = None
mime_type, encoding = calculate_mime_type(request, path, rev)
# Is this display blocked by 'binary_mime_types' configuration?
if is_binary_file_mime_type(mime_type, cfg):
raise debug.ViewVCException('Display of binary file content disabled '
'by configuration', '403 Forbidden')
# Is this a viewable image type?
if is_viewable_image(mime_type) \
and 'co' in cfg.options.allowed_views:
@@ -1840,14 +1946,19 @@ def markup_or_annotate(request, is_annotate):
}))
if cfg.options.show_log_in_markup:
options = {'svn_latest_log': 1} ### FIXME: No longer needed?
options = {
'svn_latest_log': 1, ### FIXME: Use of this magical value is uncool.
'svn_cross_copies': 1,
}
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:
@@ -2083,9 +2194,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)
@@ -2438,6 +2551,7 @@ def view_log(request):
sortby = vclib.SORTBY_DEFAULT
first = last = 0
log_pagestart = None
if cfg.options.log_pagesize:
log_pagestart = int(request.query_dict.get('log_pagestart', 0))
total = cfg.options.log_pagesextra * cfg.options.log_pagesize
@@ -2462,7 +2576,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
@@ -2470,6 +2583,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
@@ -2559,7 +2675,8 @@ def view_log(request):
if selected_rev != entry.rev:
entry.sel_for_diff_href = \
request.get_url(view_func=view_log,
params={'r1': entry.rev},
params={'r1': entry.rev,
'log_pagestart': log_pagestart},
escape=1)
if entry.prev is not None:
entry.diff_to_prev_href = \
@@ -2700,7 +2817,9 @@ def view_log(request):
if cfg.options.log_pagesize:
data['log_paging_action'], data['log_paging_hidden_values'] = \
request.get_form(params={'log_pagestart': None})
request.get_form(params={'log_pagestart': None,
'r1': selected_rev,
})
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,
@@ -2966,7 +3085,7 @@ class DiffSource:
return _item(type='header',
line_info_left=match.group(1),
line_info_right=match.group(2),
line_info_extra=match.group(3))
line_info_extra=self._format_text(match.group(3)))
if line[0] == '\\':
# \ No newline at end of file
@@ -3181,6 +3300,13 @@ def view_patch(request):
query_dict = request.query_dict
p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request)
mime_type1, encoding1 = calculate_mime_type(request, p1, rev1)
mime_type2, encoding2 = calculate_mime_type(request, p2, rev2)
if is_binary_file_mime_type(mime_type1, cfg) or \
is_binary_file_mime_type(mime_type2, cfg):
raise debug.ViewVCException('Display of binary file content disabled '
'by configuration', '403 Forbidden')
# In the absence of a format dictation in the CGI params, we'll let
# use the configured diff format, allowing 'c' to mean 'c' and
# anything else to mean 'u'.
@@ -3221,6 +3347,13 @@ def view_diff(request):
query_dict = request.query_dict
p1, p2, rev1, rev2, sym1, sym2 = setup_diff(request)
mime_type1, encoding1 = calculate_mime_type(request, p1, rev1)
mime_type2, encoding2 = calculate_mime_type(request, p2, rev2)
if is_binary_file_mime_type(mime_type1, cfg) or \
is_binary_file_mime_type(mime_type2, cfg):
raise debug.ViewVCException('Display of binary file content disabled '
'by configuration', '403 Forbidden')
# since templates are in use and subversion allows changes to the dates,
# we can't provide a strong etag
if check_freshness(request, None, '%s-%s' % (rev1, rev2), weak=1):
@@ -3318,7 +3451,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,
@@ -3334,7 +3468,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,
@@ -3582,7 +3717,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:
@@ -3705,13 +3841,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,
@@ -4069,9 +4206,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':
@@ -4365,8 +4503,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

@@ -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."
@@ -111,8 +113,13 @@ numbers, and not literal):
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).
features.
19. Merge CHANGES for this release into the CHANGES file for newer
release lines.
http://viewvc.tigris.org/ds/viewForumSummary.do?dsForumId=4253
19. Post a new release notification at Freecode.
https://freecode.com/projects/viewvc/releases/new
20. Merge CHANGES for this release into the CHANGES file for newer
release lines and commit.

View File

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