diff --git a/CHANGES b/CHANGES
index d59618c8..7f8c2c6e 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,28 @@
+4Intra.net/CUSTIS improvements
+
+ * Support for full-text search over file contents, including binary
+ documents like *.doc and so on using Sphinx Search and Apache Tika
+ server. Patched Tika with fixes for #TIKA709 and #TIKA964 is highly
+ recommended:
+ http://wiki.4intra.net/public/tika-app-1.2-fix-TIKA709-TIKA964.jar
+ (SHA1 efef722a5e2322f7c2616d096552a48134dc5faa)
+ * Access right checks in query results.
+ * Access right checks for repository root directories.
+ * New query parameters: repository, repo type, revision number.
+ * Authorizer for CVSnt ACLs.
+ * InnoDB, additional database indexes and some search query
+ optimisations.
+ * Support for specifying path to MySQL UNIX socket.
+ * Asynchronous hook examples for updating SVN and CVS repos.
+ * Slightly more correct charset guessing, especially for Russian.
+ * Support for diffing added/removed files.
+ * File lists in RSS feeds for 'classic' template.
+ * Path configuration via a single 'viewvcinstallpath.py' file,
+ not via editing multiple bin/* files.
+ * Link to repository list instead of viewvc.org from the logo
+ * "rcsfile service" support used to workaround command execution
+ problems (forks) from Apache mod_python.
+
Version 1.2.0 (released ??-???-????)
* bumped minimum support Python version to 2.4
diff --git a/INSTALL b/INSTALL
index d193c8d9..72efcd00 100644
--- a/INSTALL
+++ b/INSTALL
@@ -140,7 +140,7 @@ installation instructions.
default_root
root_as_url_component
rcs_dir
- mime_types_file
+ mime_types_files
There are some other options that are usually nice to change. See
viewvc.conf for more information. ViewVC provides a working,
diff --git a/README b/README
index a506bf1f..b434d779 100644
--- a/README
+++ b/README
@@ -1,5 +1,7 @@
ViewVC -- Viewing the content of CVS/SVN repositories with a Webbrowser.
+This is the 4Intra.net patched version with some extra features.
+
Please read the file INSTALL for more information.
And see windows/README for more information on running ViewVC on
diff --git a/lib/config.py b/lib/config.py
index e9a93acf..295c71cb 100644
--- a/lib/config.py
+++ b/lib/config.py
@@ -363,6 +363,7 @@ class Config:
if section == root_authz_section:
for key, value in self._get_parser_items(self.parser, section):
params[key] = value
+ params['__config'] = self
return authorizer, params
def get_authorizer_params(self, authorizer=None):
@@ -377,6 +378,7 @@ class Config:
sub_config = getattr(self, authz_section)
for attr in dir(sub_config):
params[attr] = getattr(sub_config, attr)
+ params['__config'] = self
return params
def guesser(self):
diff --git a/lib/cvsdb.py b/lib/cvsdb.py
index e350115c..dbc0c749 100644
--- a/lib/cvsdb.py
+++ b/lib/cvsdb.py
@@ -14,6 +14,7 @@ import sys
import time
import re
import cgi
+import string
import vclib
import dbi
@@ -530,7 +531,7 @@ class CheckinDatabase:
' AND dirid=dirs.id AND fileid=files.id' % (commits_table, commits_table, commits_table, ','.join(ids))
)
- def CreateSQLQueryString(self, query):
+ def CreateSQLQueryString(self, query, detect_leftover=0):
commits_table = self.GetCommitsTable()
fields = [
commits_table+".*",
@@ -692,7 +693,7 @@ class CheckinDatabase:
revision = rows[docid]['revision']
fp = None
try:
- fp, _ = self.request.get_repo(repo).repos.openfile(path, revision)
+ fp, _ = self.request.get_repo(repo).repos.openfile(path, revision, {})
content = fp.read()
fp.close()
content = self.guesser.utf8(content)
@@ -757,13 +758,14 @@ class CheckinDatabase:
rows = self.RunSphinxQuery(query)
else:
# Use regular queries when document content is not searched
- rows = self.selectall(self.db, self.CreateSQLQueryString(query))
+ rows = self.selectall(self.db, self.CreateSQLQueryString(query, 1))
# Check rights
rows = (r for r in rows if self.check_commit_access(
r['repository_name'],
r['dir_name'],
r['file_name'],
r['revision']))
+ query.SetExecuted()
# Convert rows to commit objects
for row in rows:
@@ -1211,15 +1213,13 @@ def CreateCommit():
def CreateCheckinQuery():
return CheckinDatabaseQuery()
-def ConnectDatabase(cfg, readonly=0):
- if readonly:
- user = cfg.cvsdb.readonly_user
- passwd = cfg.cvsdb.readonly_passwd
- else:
- user = cfg.cvsdb.user
- passwd = cfg.cvsdb.passwd
- db = CheckinDatabase(cfg.cvsdb.host, cfg.cvsdb.port, user, passwd,
- cfg.cvsdb.database_name)
+def ConnectDatabase(cfg, request=None, readonly=0):
+ db = CheckinDatabase(
+ readonly = readonly,
+ request = request,
+ cfg = cfg.cvsdb,
+ guesser = cfg.guesser(),
+ )
db.Connect()
return db
diff --git a/lib/sapi.py b/lib/sapi.py
index 585f3ca0..037cd09b 100644
--- a/lib/sapi.py
+++ b/lib/sapi.py
@@ -32,6 +32,8 @@ server = None
# that character as-is, and sometimes needs to embed escaped values
# into HTML attributes.
def escape(s):
+ try: s = s.encode('utf8')
+ except: pass
s = str(s)
s = s.replace('&', '&')
s = s.replace('>', '>')
diff --git a/lib/vcauth/__init__.py b/lib/vcauth/__init__.py
index 2f78571f..b80647fb 100644
--- a/lib/vcauth/__init__.py
+++ b/lib/vcauth/__init__.py
@@ -41,7 +41,7 @@ class GenericViewVCAuthorizer:
pass
-
+
##############################################################################
class ViewVCAuthorizer(GenericViewVCAuthorizer):
diff --git a/lib/vcauth/cvsntacl/__init__.py b/lib/vcauth/cvsntacl/__init__.py
index 42cbf8a9..1a9cf311 100644
--- a/lib/vcauth/cvsntacl/__init__.py
+++ b/lib/vcauth/cvsntacl/__init__.py
@@ -84,7 +84,7 @@ class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
def check_path_access(self, rootname, path_parts, pathtype, rev=None):
if not path_parts:
- return 1
+ return self.check(rootname, [], '')
if pathtype == vclib.DIR:
return self.check(rootname, path_parts, '')
f = path_parts[-1]
diff --git a/lib/vcauth/union/__init__.py b/lib/vcauth/union/__init__.py
deleted file mode 100644
index 61b6cae8..00000000
--- a/lib/vcauth/union/__init__.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# -*-python-*-
-#
-# Copyright (C) 2009 Vitaliy Filippov.
-#
-# By using this file, you agree to the terms and conditions set forth in
-# the LICENSE.html file which can be found at the top level of the ViewVC
-# distribution or at http://viewvc.org/license-1.html.
-#
-# For more information, visit http://viewvc.org/
-#
-# -----------------------------------------------------------------------
-
-import vcauth
-import vclib
-import string
-import debug
-
-class ViewVCAuthorizer(vcauth.GenericViewVCAuthorizer):
- """A 'union' authorizer: it makes possible to use different authorizers
- for different roots."""
-
- def __init__(self, username, params={}):
- self.username = username
- self.params = params
- self.cfg = params['__config']
- self.default = params.get('default', '')
- self.byroot = {}
- self.authz = {}
- union = params.get('union', '')
- for i in union.split(','):
- if i.find(':') < 0:
- continue
- (root, auth) = i.split(':', 2)
- self.byroot[root.strip()] = auth.strip()
-
- def create_authz(self, rootname):
- aname = self.byroot.get(rootname, '') or self.default
- if not aname:
- return None
- if self.authz.get(aname, None):
- return self.authz[aname]
- import imp
- fp = None
- try:
- try:
- fp, path, desc = imp.find_module(aname, vcauth.__path__)
- my_auth = imp.load_module('viewvc', fp, path, desc)
- except ImportError:
- raise
- finally:
- if fp:
- fp.close()
- params = self.cfg.get_authorizer_params(aname, rootname)
- self.authz[aname] = my_auth.ViewVCAuthorizer(self.username, params)
- return self.authz[aname]
-
- def check_root_access(self, rootname):
- a = self.create_authz(rootname)
- if a:
- return a.check_root_access(rootname)
- return None
-
- def check_path_access(self, rootname, path_parts, pathtype, rev=None):
- a = self.create_authz(rootname)
- if a:
- return a.check_path_access(rootname, path_parts, pathtype, rev)
- return None
diff --git a/lib/vclib/__init__.py b/lib/vclib/__init__.py
index bf1151a3..ffad38e9 100644
--- a/lib/vclib/__init__.py
+++ b/lib/vclib/__init__.py
@@ -392,7 +392,7 @@ class _diff_fp:
args.extend(["-L", self._label(info1), "-L", self._label(info2)])
args.extend([temp1, temp2])
args.insert(0, diff_cmd)
- os.system("'"+"' '".join(args)+"' &> "+self.temp3)
+ os.system("'"+"' '".join(args)+"' > '"+self.temp3+"' 2> '"+self.temp3+"'")
self.fp = open(self.temp3, 'rb')
self.fp.seek(0)
diff --git a/lib/viewvc.py b/lib/viewvc.py
index 763966b0..78cc3842 100644
--- a/lib/viewvc.py
+++ b/lib/viewvc.py
@@ -14,7 +14,7 @@
#
# -----------------------------------------------------------------------
-__version__ = '1.2-dev-2243'
+__version__ = '1.2.svn2905+4intranet-1'
# this comes from our library; measure the startup time
import debug
@@ -39,6 +39,7 @@ import types
import urllib
import datetime
import locale
+import string
# These modules come from our library (the stub has set up the path)
from common import _item, _RCSDIFF_NO_CHANGES, _RCSDIFF_IS_BINARY, _RCSDIFF_ERROR, TemplateData
@@ -152,7 +153,7 @@ class Request:
roottype, rootpath, rootname = locate_root(self.cfg, rootname)
if roottype:
# Setup an Authorizer for this rootname and username
- authorizer = setup_authorizer(self.cfg, self.username, self.rootname)
+ authorizer = setup_authorizer(self.cfg, self.username, rootname)
# Create the repository object
if roottype == 'cvs':
@@ -298,7 +299,7 @@ class Request:
debug.t_start('select-repos')
try:
if self.repos.roottype() == 'cvs':
- self.rootpath = vclib.ccvs.canonicalize_rootpath(rootpath)
+ self.rootpath = vclib.ccvs.canonicalize_rootpath(self.rootpath)
self.repos = vclib.ccvs.CVSRepository(self.rootname,
self.rootpath,
self.auth,
@@ -308,7 +309,7 @@ class Request:
# $CVSHeader$
os.environ['CVSROOT'] = self.rootpath
elif self.repos.roottype() == 'svn':
- self.rootpath = vclib.svn.canonicalize_rootpath(rootpath)
+ self.rootpath = vclib.svn.canonicalize_rootpath(self.rootpath)
self.repos = vclib.svn.SubversionRepository(self.rootname,
self.rootpath,
self.auth,
@@ -898,6 +899,7 @@ def setup_authorizer(cfg, username, rootname=None):
return None
# First, try to load a module with the configured name.
+ # FIXME FIXME FIXME This hack leads to ALL authorizers having 'viewvc.ViewVCAuthorizer' as their class name
import imp
fp = None
try:
@@ -1212,7 +1214,6 @@ _re_rewrite_url = re.compile('((http|https|ftp|file|svn|svn\+ssh)'
# Matches email addresses
_re_rewrite_email = re.compile('([-a-zA-Z0-9_.\+]+)@'
'(([-a-zA-Z0-9]+\.)+[A-Za-z]{2,4})')
-_re_rewrites_html = [ [ _re_rewrite_url, r'\1' ] ]
# Matches revision references
_re_rewrite_svnrevref = re.compile(r'\b(r|rev #?|revision #?)([0-9]+)\b')
@@ -1306,9 +1307,6 @@ class ViewVCHtmlFormatter:
trunc_s = mobj.group(1)[:maxlen]
return self._entity_encode(trunc_s), len(trunc_s)
- def format_utf8(self, mobj, userdata, maxlen=0):
- return userdata(mobj.group(0))
-
def format_svnrevref(self, mobj, userdata, maxlen=0):
"""Return a 2-tuple containing:
- the text represented by MatchObject MOBJ, formatted as an
@@ -1450,11 +1448,15 @@ class LogFormatter:
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)]
-
+
+ # UTF-8 in CVS messages.
+ if self.request.roottype == 'cvs':
+ self.log = self.request.utf8(self.log)
+
# If we are HTML-izing...
if htmlize:
# ...and we don't yet have ViewVCHtmlFormatter() object tokens...
@@ -1462,10 +1464,6 @@ class LogFormatter:
# ... then get them.
lf = ViewVCHtmlFormatter()
- # UTF-8 in CVS messages.
- if self.request.roottype == 'cvs':
- lf.add_formatter('.*', lf.format_utf8, self.request.utf8)
-
# Rewrite URLs.
lf.add_formatter(_re_rewrite_url, lf.format_url)
@@ -1611,6 +1609,7 @@ def common_template_data(request, revision=None, mime_type=None):
'tarball_href' : None,
'up_href' : None,
'username' : request.username,
+ 'env_user_url' : os.environ.get('user_url', ''),
'view' : _view_codes[request.view_func],
'view_href' : None,
'vsn' : __version__,
@@ -1866,6 +1865,7 @@ def markup_stream(request, cfg, blame_data, file_lines, filename,
c, encoding = cfg.guesser().guess_charset(content)
if encoding:
file_lines = c.rstrip('\n').split('\n')
+ file_lines = [ i+'\n' for i in file_lines ]
else:
encoding = 'unknown'
@@ -2042,7 +2042,7 @@ def markup_or_annotate(request, is_annotate):
if not mime_type or mime_type == default_mime_type:
try:
- fp, revision = request.repos.openfile(path, rev)
+ fp, revision = request.repos.openfile(path, rev, {})
mime_type = request.cfg.guesser().guess_mime(None, None, fp)
fp.close()
except:
@@ -2292,6 +2292,7 @@ def view_roots(request):
# add in the roots for the selection
roots = []
+ expand_root_parents(request.cfg)
allroots = list_roots(request)
if len(allroots):
rootnames = allroots.keys()
@@ -2795,7 +2796,7 @@ def view_log(request):
for rev in show_revs:
entry = _item()
entry.rev = rev.string
- entry.state = (cvs and rev.dead and 'dead')
+ entry.state = (request.roottype == 'cvs' and rev.dead and 'dead')
entry.author = rev.author
entry.changed = rev.changed
entry.date = make_time_string(rev.date, cfg)
@@ -4392,7 +4393,8 @@ def validate_query_args(request):
# First, make sure the the XXX_match args have valid values:
arg_match = arg_base + '_match'
arg_match_value = request.query_dict.get(arg_match, 'exact')
- if not arg_match_value in ('exact', 'like', 'glob', 'regex', 'notregex'):
+ if not arg_match_value in ('exact', 'like', 'glob', 'regex', 'notregex') and \
+ (arg_base != 'comment' or arg_match_value != 'fulltext'):
raise debug.ViewVCException(
'An illegal value was provided for the "%s" parameter.'
% (arg_match),
@@ -4442,8 +4444,8 @@ def view_queryform(request):
data = common_template_data(request)
data.merge(TemplateData({
- 'repos' : request.server.escape(repos),
- 'repos_match' : request.server.escape(repos_match),
+ 'repos' : request.server.escape(repos or ''),
+ 'repos_match' : request.server.escape(repos_match or ''),
'repos_type' : escaped_query_dict_get('repos_type', ''),
'query_revision' : escaped_query_dict_get('query_revision', ''),
'search_content' : escaped_query_dict_get('search_content', ''),
@@ -4855,8 +4857,8 @@ def query_patch(request, commits):
'400 Bad Request')
server_fp.write('Index: %s\n===================================================================\n' % (file))
try:
- rdate1, _, _, _ = repos.revinfo(rev1)
- rdate2, _, _, _ = repos.revinfo(rev2)
+ rdate1, _, _, _, _ = repos.revinfo(rev1)
+ rdate2, _, _, _, _ = repos.revinfo(rev2)
rdate1 = datetime.date.fromtimestamp(rdate1).strftime(' %Y/%m/%d %H:%M:%S')
rdate2 = datetime.date.fromtimestamp(rdate2).strftime(' %Y/%m/%d %H:%M:%S')
except vclib.UnsupportedFeature:
@@ -4868,7 +4870,7 @@ def query_patch(request, commits):
p2 = _path_parts(repos.get_location(file, rev2, rev2))
else:
p2 = _path_parts(file)
- fd, fr = repos.openfile(p2, rev2)
+ fd, fr = repos.openfile(p2, rev2, {})
if not fd or rev2 != fr:
raise vclib.ItemNotFound(p2)
if fd:
@@ -4886,7 +4888,7 @@ def query_patch(request, commits):
p1 = _path_parts(repos.get_location(p1, rev1, rev1))
else:
p1 = _path_parts(file)
- fd, fr = repos.openfile(p1, rev1)
+ fd, fr = repos.openfile(p1, rev1, {})
if fd:
fd.close()
rev1 = fr
@@ -5290,16 +5292,19 @@ def find_root_in_parents(cfg, rootname, roottype):
if repo_type != roottype:
continue
pp = os.path.normpath(pp[:pos].strip())
-
- rootpath = None
- if roottype == 'cvs':
- rootpath = vclib.ccvs.find_root_in_parent(pp, rootname)
- elif roottype == 'svn':
- rootpath = vclib.svn.find_root_in_parent(pp, rootname)
- if rootpath is not None:
- return rootpath
- return None
+ if roottype == 'cvs':
+ roots = vclib.ccvs.expand_root_parent(pp)
+ elif roottype == 'svn':
+ roots = vclib.svn.expand_root_parent(pp)
+ else:
+ roots = {}
+ if roots.has_key(rootname):
+ return roots[rootname], rootname
+ for (k, v) in roots.iteritems():
+ if v == rootname:
+ return rootname, k
+ return None, None
def locate_root(cfg, rootname):
"""Return a 3-tuple ROOTTYPE, ROOTPATH, ROOTNAME for configured ROOTNAME.
diff --git a/notes/TODO b/notes/TODO
new file mode 100644
index 00000000..c9c36e25
--- /dev/null
+++ b/notes/TODO
@@ -0,0 +1,53 @@
+PREFACE
+-------
+This file will go away soon after release 0.8. Please use the SourceForge
+tracker to resubmit any of the items listed below, if you think, it is
+still an issue:
+ http://sourceforge.net/tracker/?group_id=18760
+Before reporting please check, whether someone else has already done this.
+Working patches increase the chance to be included into the next release.
+ -- PeFu / October 2001
+
+TODO ITEMS
+----------
+*) add Tamminen Eero's comments on how to make Linux directly execute
+ the Python script. From email on Feb 19.
+ [ add other examples, such as my /bin/sh hack or the teeny CGI stub
+ importing the bulk hack ]
+
+*) insert rcs_path into PATH before calling "rcsdiff". rcsdiff might
+ use "co" and needs to find it on the path.
+
+*) show the "locked" flag (attach it to the LogEntry objects).
+ Idea from Russell Gordon
+
+*) committing with a specific revision number:
+ http://mailman.lyra.org/pipermail/viewcvs/2000q1/000008.html
+
+*) add capability similar to cvs2cl.pl:
+ http://mailman.lyra.org/pipermail/viewcvs/2000q2/000050.html
+ suggestion from Chris Meyer .
+
+*) add a tree view of the directory structure (and files?)
+
+*) include a ConfigParser.py to help older Python installations
+
+*) add a check for the rcs programs/paths to viewvc-install. clarify the
+ dependency on RCS in the docs.
+
+*) have a "check" mode that verifies binaries are available on rcs_path
+
+ -> alternately (probably?): use rcsparse rather than external tools
+
+KNOWN BUGS
+----------
+*) time.timezone seems to not be available on some 1.5.2 installs.
+ I was unable to verify this. On RedHat and SuSE Linux this bug
+ is non existant.
+
+*) With old repositories containing many branches, tags or thousands
+ or revisions, the cvsgraph feature becomes unusable (see INSTALL).
+ ViewVC can't do much about this, but it might be possible to
+ investigate the number of branches, tags and revision in advance
+ and disable the cvsgraph links, if the numbers exceed a certain
+ treshold.
diff --git a/notes/authz-dev-TODO b/notes/authz-dev-TODO
new file mode 100644
index 00000000..4d885794
--- /dev/null
+++ b/notes/authz-dev-TODO
@@ -0,0 +1,82 @@
+Here lie TODO items for the pluggable authz system:
+
+ * Subversion uses path privelege to determine visibility of revision
+ metadata. That logic is pretty Subversion-specific, so it feels like it
+ belongs outside the vcauth library as just a helper function in viewvc.py
+ or something. The algorithm is something like this (culled from the
+ CollabNet implementation, and not expected to work as edited):
+
+ # Subversion revision access levels
+ REVISION_ACCESS_NONE = 0
+ REVISION_ACCESS_PARTIAL = 1
+ REVISION_ACCESS_FULL = 2
+
+ def check_svn_revision_access(request, rev):
+ # Check our revision access cache first.
+ if request.rev_access_cache.has_key(rev):
+ return request.rev_access_cache[rev]
+
+ # Check our cached answer to the question "Does the user have
+ # an all-access or a not-at-all-access pass?"
+ if request.full_access is not None:
+ return request.full_access \
+ and REVISION_ACCESS_FULL or REVISION_ACCESS_NONE
+
+ # Get a list of paths changed in REV.
+ ### FIXME: There outta be a vclib-complaint way to do this,
+ ### as this won't work for vclib.svn_ra.
+ import svn.fs
+ rev_root = svn.fs.revision_root(self.repos.fs_ptr, rev)
+ changes = svn.fs.paths_changed(rev_root)
+
+ # Loop over the list of changed paths, asking the access question
+ # for each one. We'll track whether we've found any readable paths
+ # as well as any un-readable (non-authorized) paths, and quit
+ # checking as soon as we know our revision access level.
+ found_readable = 0
+ found_unreadable = 0
+ for path in changes.keys():
+ parts = _path_parts(path)
+ kind = request.repos.itemtype(parts, rev)
+ if kind == vclib.DIR:
+ access = request.auth.check_dir_access(parts, rev)
+ elif:
+ access = request.auth.check_file_access(parts, rev)
+ if access:
+ found_readable = 1
+ else:
+ found_unreadable = 1
+ # Optimization: if we've found at least one readable, and one
+ # unreadable, we needn't ask about any more paths.
+ if found_readable and found_unreadable:
+ break
+
+ # If there are paths but we can't read any of them, no access is
+ # granted.
+ if len(changes) and not found_readable:
+ request.rev_access_cache[rev] = REVISION_ACCESS_NONE
+ # If we found at least one unreadable path, partial access is
+ # granted.
+ elif found_unreadable:
+ request.rev_access_cache[rev] = REVISION_ACCESS_PARTIAL
+ # Finally, if there were no paths at all, or none of the existing
+ # ones were unreadable, grant full access.
+ else:
+ request.rev_access_cache[rev] = REVISION_ACCESS_FULL
+ return request.rev_access_cache[rev]
+
+ The problems are: where does one hang the revision access cache
+ so that it doesn't survive a given request? On the request, as
+ shown in the edited code above?
+
+ Can we actually get a good interface into the vcauth layer for
+ asking the all-access / no-access question? Obviously each vcauth
+ provider can cache that value for itself and use it as it deems
+ necessary, but ideally the revision access level question will
+ want to know if said auth provider was able to answer the question
+ and, if so, what the answer was.
+
+ Another off-the-wall idea -- let's just teach Subversion to do
+ this calculation as part of a libsvn_repos API.
+
+
diff --git a/notes/logo/viewvc-logo.odg b/notes/logo/viewvc-logo.odg
new file mode 100644
index 00000000..8516ec97
Binary files /dev/null and b/notes/logo/viewvc-logo.odg differ
diff --git a/notes/logo/viewvc-logo.pdf b/notes/logo/viewvc-logo.pdf
new file mode 100644
index 00000000..927ab370
Binary files /dev/null and b/notes/logo/viewvc-logo.pdf differ
diff --git a/notes/logo/viewvc-logo.svg b/notes/logo/viewvc-logo.svg
new file mode 100644
index 00000000..b67df99d
--- /dev/null
+++ b/notes/logo/viewvc-logo.svg
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/notes/vclib-enhancements.txt b/notes/vclib-enhancements.txt
new file mode 100644
index 00000000..627e23df
--- /dev/null
+++ b/notes/vclib-enhancements.txt
@@ -0,0 +1,78 @@
+The following is an email from a developer who was integrating bzr
+into ViewVC in which he shares some thoughts on how to further
+abstract the version control system interactions into first-class APIs
+in the vclib module.
+
+ Subject: Re: [ViewCVS-dev] difflib module
+ Date: Wed, 1 Jun 2005 16:59:10 -0800
+ From: "Johan Rydberg"
+ To: "Michael Pilato"
+ Cc:
+
+ "C. Michael Pilato" writes:
+
+ >> I've tried to minimize the changes to the viewcvs.py, but of course
+ >> there are a few places where some things has to be altered.
+ >
+ > Well, if along the way, you have ideas about how to further abstract
+ > stuff into the vclib/ modules, please post.
+
+ I came up with a few as off now;
+
+ * Generalize revision counting; svn starts from 0, bzr starts from 1.
+ Can be done by a constant; request.repos.MIN_REVNO. For CVS I'm not
+ sure exactly what should be done. Right now this is only used in
+ view_revision_svn, so it is not a problem in the short term.
+
+ * Generalize view_diff;
+
+ * Have a repo-method diff(file1, rev1, file2, rev2, args) that returns
+ (date1, date2, fp). Means human_readbale_diff and raw_diff does not
+ have to parse dates. Good for VCS that does not have the date in
+ the diff. [### DONE ###]
+
+ * I'm not sure you should require GNU diff. Some VCS may use own
+ diff mechanisms (bzr uses difflib, _or_ GNU diff when needed.
+ Monotone uses its own, IIRC.)
+
+ * Generalize view_revision ;
+
+ * Have a method, revision_info(), which returns (date, author, msg,
+ changes) much like vclib.svn.get_revision_info. The CVS version
+ can raise a ViewCVSException. [### DONE ###]
+
+ * Establish a convention for renamed/copied files; current should
+ work good enough (change.base_path, change.base_rev) but action
+ string must be same for both svn and others.
+
+ * request.repos.rev (or .revision) should give the current revision
+ number of the repo. No need for this (from view_directory):
+
+ if request.roottype == 'svn':
+ revision = str(vclib.svn.created_rev(request.repos, request.where))
+
+ If svn needs the full name of the repo, why not give it when the
+ repo is created?
+
+ * request.repos.youngest vs vclib.svn.get_youngest_revision(REPO)
+
+ * More object oriented;
+
+ * The subversion backend is not really object oriented. viewcfg.py uses
+ a lot function from vclib.svn, which could instead be methods of the
+ Repository class. Example:
+
+ diffobj = vclib.svn.do_diff(request.repos, p1, int(rev1),
+ p2, int(rev2), args)
+
+ This should be a method of the repository;
+
+ diffobj = request.repos.do_diff(p1, rev1, ...)
+
+ I have identified the following functions;
+
+ - vclib.svn.created_rev
+ - vclib.svn.get_youngest_revision
+ - vclib.svn.date_from_rev
+ - vclib.svn.do_diff
+ - vclib.svn.get_revision_info
diff --git a/templates/classic/include/header.ezt b/templates/classic/include/header.ezt
index bfb9019d..d6527956 100644
--- a/templates/classic/include/header.ezt
+++ b/templates/classic/include/header.ezt
@@ -17,7 +17,7 @@
-