Finish issue #57 with more support for showing locked status.

* lib/vclib/__init__.py
  (Repository.dirlogs): Update comment.
  (Revision.__init__): Add 'lockinfo' parameter, used to populate
    similarly named class member.

* lib/vclib/svn/svn_repos.py
  (Revision.__init__): Add 'lockinfo' parameter, and update call to
    vclib.Revision().
  (_log_helper): Update call to Revision(), and lose custom lockinfo handling.
  (LocalSubversionRepository.dirlogs): Populate entry lockinfo with a
    call to svn_fs_get_lock() for each entry.

* lib/vclib/svn/svn_ra.py
  (LogCollector.__init__): Add 'lockinfo' parameter, used to populate
    similarly named class member.
  (LogCollector.add_log): Pass self.lockinfo to updated call to Revision().
  (RemoteSubversionRepository.itemlog): Pass lock info to
    LogCollector(), and lose custom lockinfo handling.
  (RemoteSubversionRepository.itemtype, RemoteSubversionRepository.listdir): 
    Update expected return value from _get_dirents().
  (RemoteSubversionRepository.dirlogs): Populate entry lockinfo from
    updated return value from _get_dirents().
  (RemoteSubversionRepository._get_dirents): Rework to trade in
    dirents and locks instead of only dirents.

* lib/vclib/ccvs/bincvs.py
  (Revision.__init__): Update call to vclib.Revision().
  (_get_logs): Add 'lockinfo' member to DirEntry() items.

* lib/vclib/ccvs/ccvs.py
  (InfoSink.__init__): Init lockinfo dictionary.
  (InfoSink.set_locker): New.
  (InfoSink.define_revision): Set lockinfo on Revision item.
  (InfoSink.set_revision_info): Populate DirEntry lockinfo from
    Revision lockinfo data.

* lib/viewvc.py
  (view_directory): Populate entry lockinfo.
  (common_template_data): Populate the 'lockinfo' data dictionary item.

* templates/directory.ezt,
* templates/dir_new.ezt
* templates/annotate.ezt,
* templates/markup.ezt
  Tweak to show lock status.

* docs/template-authoring-guide.html
  Note new data dictionary items.

git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@1787 8cb11bc2-c004-0410-86c3-e597b4017df7
remotes/merged-file-views
cmpilato 2008-02-12 21:06:40 +00:00
parent d620498e9e
commit 3cbf07551b
11 changed files with 78 additions and 28 deletions

View File

@ -159,6 +159,11 @@ td {
<var>labels</var> section of the key/value file whose configured
abstract name is <var>l10n</var>.</td>
</tr>
<tr class="varlevel1">
<td class="varname">lockinfo</td>
<td>String</td>
<td>Information about the lock status of the current resource.</td>
</tr>
<tr class="varlevel1">
<td class="varname">log_href</td>
<td>String</td>
@ -928,6 +933,11 @@ td {
<td>URL of the ViewVC revision graph view for the directory
entry.</td>
</tr>
<tr class="varlevel2">
<td class="varname">entries.lockinfo</td>
<td>String</td>
<td>Information about the lock status of the directory entry.</td>
</tr>
<tr class="varlevel2">
<td class="varname">entries.log</td>
<td>String</td>

View File

@ -101,7 +101,7 @@ class Repository:
New properties will be set on all of the DirEntry objects in the entries
list. At the very least, a "rev" property will be set to a revision
number or None if the entry doesn't have a number. Other properties that
may be set include "date", "author", and "log".
may be set include "date", "author", "log", "size", and "lockinfo".
The path is specified as a list of components, relative to the root
of the repository. e.g. ["subdir1", "subdir2", "filename"]
@ -180,7 +180,7 @@ class DirEntry:
class Revision:
"""Instances holds information about revisions of versioned resources"""
def __init__(self, number, string, date, author, changed, log, size):
def __init__(self, number, string, date, author, changed, log, size, lockinfo):
"""Create a new Revision() item:
NUMBER: Revision in an integer-based, sortable format
STRING: Revision as a string
@ -189,6 +189,7 @@ class Revision:
CHANGED: Lines-changed (contextual diff) information
LOG: Log message associated with the creation of this revision
SIZE: Size (in bytes) of this revision's fulltext (files only)
LOCKINFO: Information about locks held on this revision
"""
self.number = number
self.string = string
@ -197,6 +198,7 @@ class Revision:
self.changed = changed
self.log = log
self.size = size
self.lockinfo = lockinfo
def __cmp__(self, other):
return cmp(self.number, other.number)

View File

@ -346,7 +346,7 @@ class Revision(vclib.Revision):
def __init__(self, revstr, date=None, author=None, dead=None,
changed=None, log=None):
vclib.Revision.__init__(self, _revision_tuple(revstr), revstr,
date, author, changed, log, None)
date, author, changed, log, None, None)
self.dead = dead
class Tag:
@ -1042,6 +1042,7 @@ def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs):
file.author = wanted_entry.author
file.dead = file.kind == vclib.FILE and wanted_entry.dead
file.log = wanted_entry.log
file.lockinfo = lockinfo.get(file.rev)
# suppress rlog errors if we find a usable revision in the end
del file.errors[:]
elif file.kind == vclib.FILE:

View File

@ -164,6 +164,7 @@ class InfoSink(MatchingSink):
self.alltags = alltags
self.matching_rev = None
self.perfect_match = 0
self.lockinfo = { }
def define_tag(self, name, revision):
MatchingSink.define_tag(self, name, revision)
@ -175,13 +176,17 @@ class InfoSink(MatchingSink):
# tag we're looking for doesn't exist
raise rcsparse.RCSStopParser
def set_locker(self, rev, locker):
self.lockinfo[rev] = locker
def define_revision(self, revision, date, author, state, branches, next):
if self.perfect_match:
return
tag = self.find_tag
rev = Revision(revision, date, author, state == "dead")
rev.lockinfo = self.lockinfo.get(revision)
# perfect match if revision number matches tag number or if revision is on
# trunk and tag points to trunk. imperfect match if tag refers to a branch
# and this revision is the highest revision so far found on that branch
@ -200,6 +205,7 @@ class InfoSink(MatchingSink):
self.entry.date = self.matching_rev.date
self.entry.author = self.matching_rev.author
self.entry.dead = self.matching_rev.dead
self.entry.lockinfo = self.matching_rev.lockinfo
self.entry.log = log
raise rcsparse.RCSStopParser
else:

View File

@ -91,7 +91,7 @@ def _get_rev_details(svnrepos, rev):
class LogCollector:
def __init__(self, path, show_all_logs):
def __init__(self, path, show_all_logs, lockinfo):
# This class uses leading slashes for paths internally
if not path:
self.path = '/'
@ -99,6 +99,7 @@ class LogCollector:
self.path = path[0] == '/' and path or '/' + path
self.logs = []
self.show_all_logs = show_all_logs
self.lockinfo = lockinfo
def add_log(self, paths, revision, author, date, message, pool):
# Changed paths have leading slashes
@ -121,7 +122,7 @@ class LogCollector:
this_path = change.copyfrom_path + self.path[len(changed_path):]
if self.show_all_logs or this_path:
entry = Revision(revision, _datestr_to_date(date), author, message, None,
self.path[1:], None, None)
self.lockinfo, self.path[1:], None, None)
self.logs.append(entry)
if this_path:
self.path = this_path
@ -217,7 +218,7 @@ class RemoteSubversionRepository(vclib.Repository):
rev = self._getrev(rev)
if not len(path_parts):
return vclib.DIR
dirents = self._get_dirents(path, rev)
dirents, locks = self._get_dirents(path, rev)
try:
entry = dirents[path_parts[-1]]
if entry.kind == core.svn_node_dir:
@ -243,7 +244,7 @@ class RemoteSubversionRepository(vclib.Repository):
path = self._getpath(path_parts)
rev = self._getrev(rev)
entries = [ ]
dirents = self._get_dirents(path, rev)
dirents, locks = self._get_dirents(path, rev)
for name in dirents.keys():
entry = dirents[name]
if entry.kind == core.svn_node_dir:
@ -255,7 +256,8 @@ class RemoteSubversionRepository(vclib.Repository):
def dirlogs(self, path_parts, rev, entries, options):
rev_info_cache = { }
dirents = self._get_dirents(self._getpath(path_parts), self._getrev(rev))
dirents, locks = self._get_dirents(self._getpath(path_parts),
self._getrev(rev))
for entry in entries:
dirent = dirents[entry.name]
if rev_info_cache.has_key(dirent.created_rev):
@ -270,23 +272,28 @@ class RemoteSubversionRepository(vclib.Repository):
entry.date = _datestr_to_date(date)
entry.log = log
entry.size = dirent.size
entry.lockinfo = None
if locks.has_key(entry.name):
entry.lockinfo = locks[entry.name].owner
def itemlog(self, path_parts, rev, options):
full_name = self._getpath(path_parts)
rev = self._getrev(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(full_name, options.get('svn_show_all_dir_logs', 0))
dir_url = self.rootpath
if full_name:
dir_url = dir_url + '/' + full_name
# Use ls3 to fetch the lock status for this item.
lockinfo = None
dirents, locks = client.svn_client_ls3(dir_url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
locker = locks.has_key(path_parts[-1]) \
and locks[path_parts[-1]].owner or ''
if locks.has_key(path_parts[-1]):
lockinfo = locks[path_parts[-1]].owner
# It's okay if we're told to not show all logs on a file -- all
# the revisions should match correctly anyway.
lc = LogCollector(full_name, options.get('svn_show_all_dir_logs', 0),
lockinfo)
cross_copies = options.get('svn_cross_copies', 0)
client.svn_client_log([dir_url], _rev2optrev(rev), _rev2optrev(1),
@ -295,7 +302,6 @@ class RemoteSubversionRepository(vclib.Repository):
revs.sort()
prev = None
for rev in revs:
rev.lockinfo = locker
rev.prev = prev
prev = rev
@ -358,18 +364,22 @@ class RemoteSubversionRepository(vclib.Repository):
return rev
def _get_dirents(self, path, rev):
"""Return a 2-type of dirents and locks, possibly reading/writing
from a local cache of that information."""
if path:
key = str(rev) + '/' + path
dir_url = self.rootpath + '/' + path
else:
key = str(rev)
dir_url = self.rootpath
dirents = self._dirent_cache.get(key)
if dirents:
return dirents
dirents = client.svn_client_ls(dir_url, _rev2optrev(rev), 0, self.ctx)
self._dirent_cache[key] = dirents
return dirents
dirents_locks = self._dirent_cache.get(key)
if not dirents_locks:
dirents, locks = client.svn_client_ls3(dir_url, _rev2optrev(rev),
_rev2optrev(rev), 0, self.ctx)
dirents_locks = [dirents, locks]
self._dirent_cache[key] = dirents_locks
return dirents_locks[0], dirents_locks[1]
##--- custom --##

View File

@ -110,9 +110,10 @@ def _datestr_to_date(datestr):
class Revision(vclib.Revision):
"Hold state for each revision's log entry."
def __init__(self, rev, date, author, msg, size,
def __init__(self, rev, date, author, msg, size, lockinfo,
filename, copy_path, copy_rev):
vclib.Revision.__init__(self, rev, str(rev), date, author, None, msg, size)
vclib.Revision.__init__(self, rev, str(rev), date, author, None,
msg, size, lockinfo)
self.filename = filename
self.copy_path = copy_path
self.copy_rev = copy_rev
@ -186,10 +187,9 @@ def _log_helper(svnrepos, rev, path, lockinfo):
size = fs.file_length(rev_root, path)
else:
size = None
entry = Revision(rev, date, author, msg, size, path,
entry = Revision(rev, date, author, msg, size, lockinfo, path,
copyfrom_path and _cleanup_path(copyfrom_path),
copyfrom_rev)
entry.lockinfo = lockinfo
return entry
@ -462,6 +462,8 @@ class LocalSubversionRepository(vclib.Repository):
entry.log = msg
if entry.kind == vclib.FILE:
entry.size = fs.file_length(fsroot, path)
lock = fs.get_lock(self.fs_ptr, path)
entry.lockinfo = lock and lock.owner or None
def itemlog(self, path_parts, rev, options):
"""see vclib.Repository.itemlog docstring

View File

@ -1099,6 +1099,7 @@ def common_template_data(request):
'nav_path' : nav_path(request),
'view' : _view_codes[request.view_func],
'rev' : None,
'lockinfo' : None,
'view_href' : None,
'annotate_href' : None,
'download_href' : None,
@ -1172,6 +1173,15 @@ def common_template_data(request):
if request.roottype == 'cvs' and cfg.options.use_cvsgraph:
data['graph_href'] = request.get_url(view_func=view_cvsgraph,
params={}, escape=1)
file_data = request.repos.listdir(request.path_parts[:-1],
request.pathrev, {})
def _only_this_file(item):
return item.name == request.path_parts[-1]
entries = filter(_only_this_file, file_data)
if len(entries) == 1:
request.repos.dirlogs(request.path_parts[:-1], request.pathrev,
entries, {})
data['lockinfo'] = entries[0].lockinfo
elif request.pathtype == vclib.DIR:
data['view_href'] = request.get_url(view_func=view_directory,
params={}, escape=1)
@ -1725,7 +1735,7 @@ def view_directory(request):
if cfg.options.show_logs:
row.short_log = format_log(file.log, cfg)
row.log = htmlify(file.log, cfg.options.mangle_email_addresses)
row.lockinfo = file.lockinfo
row.anchor = request.server.escape(file.name)
row.name = request.server.escape(file.name)
row.pathtype = (file.kind == vclib.FILE and 'file') or \

View File

@ -37,6 +37,9 @@ Revision [if-any revision_href]<a href="[revision_href]"><strong>[rev]</strong><
[is roottype "svn"][if-any size]
<br />File size: [size] byte(s)
[end][end]
[if-any lockinfo]
<br />Lock status: <img src="[docroot]/images/lock.png" alt="Locked" width="16" height="16" /> [lockinfo]
[end]
[is state "dead"]
<br /><strong><em>FILE REMOVED</em></strong>
[end]

View File

@ -43,6 +43,7 @@
<a name="[entries.anchor]" href="[is entries.pathtype "dir"][entries.view_href][else][if-any entries.prefer_markup][entries.view_href][else][entries.download_href][end][end]" title="[is entries.pathtype "dir"]View Directory Contents[else][if-any entries.prefer_markup]View[else]Download[end] File Contents[end]">
<img src="[docroot]/images/[is entries.pathtype "dir"]dir[else][is entries.state "dead"]broken[else]text[end][end].png" alt="" class="vc_icon" />
[entries.name][is entries.pathtype "dir"]/[end]</a>
[if-any entries.lockinfo]<img src="[docroot]/images/lock.png" alt="locked" class="vc_icon" title="Locked by [entries.lockinfo]" />[end]
[is entries.state "dead"](dead)[end]
</td>

View File

@ -97,7 +97,9 @@
<td>&nbsp;[if-any entries.rev]<a href="[entries.log_href]" title="View directory revision log"><strong>[entries.rev]</strong></a>[end]</td>
[else]
[define rev_href][if-any entries.prefer_markup][entries.view_href][else][if-any entries.download_href][entries.download_href][end][end][end]
<td>&nbsp;[if-any entries.rev][if-any rev_href]<a href="[rev_href]" title="[if-any entries.prefer_markup]View[else]Download[end] file contents">[end]<strong>[entries.rev]</strong>[if-any rev_href]</a>[end][end]</td>
<td style="white-space: nowrap;">&nbsp;[if-any entries.rev][if-any rev_href]<a href="[rev_href]" title="[if-any entries.prefer_markup]View[else]Download[end] file contents">[end]<strong>[entries.rev]</strong>[if-any rev_href]</a>[end][end]
[if-any entries.lockinfo]<img src="[docroot]/images/lock.png" alt="locked" class="vc_icon" title="Locked by [entries.lockinfo]" />[end]
</td>
[end]
<td>&nbsp;[entries.ago]</td>
<td>&nbsp;[entries.author]</td>

View File

@ -37,6 +37,9 @@ Revision [if-any revision_href]<a href="[revision_href]"><strong>[rev]</strong><
[is roottype "svn"][if-any size]
<br />File size: [size] byte(s)
[end][end]
[if-any lockinfo]
<br />Lock status: <img src="[docroot]/images/lock.png" alt="Locked" width="16" height="16" /> [lockinfo]
[end]
[is state "dead"]
<br /><strong><em>FILE REMOVED</em></strong>
[end]