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 @@ + +Master slideSlideDrawingDrawingDrawingDrawingDrawingDrawingDrawingDrawingDrawing \ 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 @@ -
ViewVC logotype
+
ViewVC logotype

[page_title]

diff --git a/templates/classic/rss.ezt b/templates/classic/rss.ezt index c3c9ae00..1415d026 100644 --- a/templates/classic/rss.ezt +++ b/templates/classic/rss.ezt @@ -11,7 +11,43 @@ [if-any commits.rss_url][commits.rss_url][end] [commits.author] [if-any commits.rss_date][commits.rss_date][else](unknown date)[end] - <pre>[format "xml"][format "html"][commits.log][end][end]</pre> + + [format "xml"] + <pre>[commits.log]</pre> + <table> + [for commits.files] + <tr> + <td style="vertical-align: top;"> + [define rev_href][if-any commits.files.prefer_markup][commits.files.view_href][else][if-any commits.files.download_href][commits.files.download_href][end][end][end] + [if-any commits.files.rev][if-any rev_href]<a href="[rev_href]">[end][commits.files.rev][if-any rev_href]</a>[end][else]&nbsp;[end] + </td> + <td style="vertical-align: top;"> + <a href="[commits.files.dir_href]">[commits.files.dir]/</a> + <a href="[commits.files.log_href]">[commits.files.file]</a> + </td> + <td style="vertical-align: top;"> + [is commits.files.type "Add"]<ins>[end] + [is commits.files.type "Change"]<a href="[commits.files.diff_href]">[end] + [is commits.files.type "Remove"]<del>[end] + [commits.files.plus]/[commits.files.minus] + [is commits.files.type "Add"]</ins>[end] + [is commits.files.type "Change"]</a>[end] + [is commits.files.type "Remove"]</del>[end] + </td> + </tr> + [end] + [if-any commits.limited_files] + <tr class="vc_row_[if-index commits even]even[else]odd[end]"> + <td>&nbsp;</td> + <td colspan="5"> + <strong><em><small>Only first [commits.num_files] files shown. + <a href="[limit_changes_href]">Show all files</a> or + <a href="[queryform_href]">adjust limit</a>.</small></em></strong> + </tr> + [end] + </table> + [end] + [end] diff --git a/templates/default/include/header.ezt b/templates/default/include/header.ezt index 3b08eaff..d979b22b 100644 --- a/templates/default/include/header.ezt +++ b/templates/default/include/header.ezt @@ -23,7 +23,7 @@
diff --git a/templates/default/query_form.ezt b/templates/default/query_form.ezt index 8060d0ba..b8b0d7d9 100644 --- a/templates/default/query_form.ezt +++ b/templates/default/query_form.ezt @@ -1,39 +1,82 @@ [# setup page definitions] - [define page_title]Query on:[end] + [define page_title]Query on /[where][end] [define help_href][docroot]/help_rootview.html[end] [# end] + [include "include/header.ezt" "query"] +

+Directory +Browse Directory

+
[for query_hidden_values][end] + [if-any enable_search_content] + + + + + [end] + + + + [is roottype "cvs"] [# For subversion, the branch field is not used ] @@ -41,85 +84,97 @@ + + + + @@ -128,8 +183,10 @@ @@ -186,7 +243,8 @@ diff --git a/templates/default/query_results.ezt b/templates/default/query_results.ezt index 5f8b85ba..e0cda9c3 100644 --- a/templates/default/query_results.ezt +++ b/templates/default/query_results.ezt @@ -16,8 +16,19 @@ query capabilities, or asking your administrator to raise the database response size threshold.

[end] -

Modify query

+

Modify query[if-any repos_root] [else] + [if-any repos_type] + [is repos_type "cvs"]| Look only in SVN | Look in all repos [end] + [is repos_type "svn"]| Look only in CVS | Look in all repos [end] + [else] + | Look only in SVN | Look only in CVS + [end] +[end]

Show commands which could be used to back out these changes

+

+ Show a patch built from these changes + [if-any patch_unsecure]
CAUTION: selected changes are not contiguous, patch may include differences from other commits.[end] +

+[plus_count]/-[minus_count] lines changed.

diff --git a/tests/testparse.py b/tests/testparse.py new file mode 100644 index 00000000..ceafe96d --- /dev/null +++ b/tests/testparse.py @@ -0,0 +1,51 @@ + +MODULE = '/home/gstein/testing/cvsroot/mod_dav' +OUTPUT = 'rlog-dump' + +import sys +sys.path.insert(0, '../lib') + +import os +import rlog + + +def get_files(root): + all_files = [ ] + os.path.walk(root, _collect_files, all_files) + all_files.sort() + return all_files + +def _collect_files(all_files, dir, files): + for f in files: + if f[-2:] == ',v': + all_files.append(os.path.join(dir, f)) + +def get_config(): + class _blank: + pass + cfg = _blank() + cfg.general = _blank() + cfg.general.rcs_path = '' + return cfg + + +def gen_dump(cfg, out_fname, files, func): + out = open(out_fname, 'w') + for f in files: + data = func(cfg, f) + out.write(data.filename + '\n') + tags = data.symbolic_name_hash.keys() + tags.sort() + for t in tags: + out.write('%s:%s\n' % (t, data.symbolic_name_hash[t])) + for e in data.rlog_entry_list: + names = dir(e) + names.sort() + for n in names: + out.write('%s=%s\n' % (n, getattr(e, n))) + +def _test(): + cfg = get_config() + files = get_files(MODULE) + gen_dump(cfg, OUTPUT + '.old', files, rlog.GetRLogData) + gen_dump(cfg, OUTPUT + '.new', files, rlog.get_data) diff --git a/tests/vclib/co.py b/tests/vclib/co.py new file mode 100755 index 00000000..b5af5946 --- /dev/null +++ b/tests/vclib/co.py @@ -0,0 +1,53 @@ +#!/usr/local/bin/python +import sys, os.path +sys.path.append( os.path.normpath(os.path.join(sys.path[0],"..","..","lib")) ) +import vclib.ccvs +import popen +def usage(): + print """ + co simulation using vclib!!! + python co.py <(relative) Path to file> + """ + sys.exit() +def convertpath(s): + a=(s,'') + res=[] + while (a[0]!=''): + a=os.path.split(a[0]) + res= [a[1]]+res + return res + +def compareco(repo,file,rev): + a=vclib.ccvs.CVSRepository("lucas",repo) + f=a.getfile(convertpath(file)) # example: ["kdelibs","po","Attic","nl.po"] + r=f.tree[rev] + fp1 = r.checkout() + fp2 = popen.popen('co', + ('-p'+rev, os.path.join(repo,file) ), 'r') + l1 = fp1.readlines() + l2 = fp2.readlines() + ok=1 + for i in range(0,len(l1)-1): + if l1[i] != l2[i+2]: + print " Difference in line %d"% i + print " line from CCVS %s" % l1[i] + print " line from RCS %s" % l2[i+2] + ok=0 + return ok + +if len(sys.argv)==4: + compareco(sys.argv[1],sys.argv[2],sys.argv[3]) +elif len(sys.argv)==3: + a=vclib.ccvs.CVSRepository("lucas",sys.argv[1]) + f=a.getfile(convertpath(sys.argv[2])) # example: ["kdelibs","po","Attic","nl.po"] + for rev in f.tree.keys(): + print ("revision: %s" % rev), + if compareco(sys.argv[1],sys.argv[2],rev): + print "ok" + else: + print "fail" + +else: + usage() + + \ No newline at end of file diff --git a/tests/vclib/rlog.py b/tests/vclib/rlog.py new file mode 100644 index 00000000..dc5211b1 --- /dev/null +++ b/tests/vclib/rlog.py @@ -0,0 +1,5 @@ +#!/usr/local/bin/python +import sys, os.path +sys.path.append( os.path.normpath(os.path.join(sys.path[0],"..","..","lib")) ) +import vclib.ccvs +import popen diff --git a/viewvc-install b/viewvc-install index c52b05aa..fd1b5270 100755 --- a/viewvc-install +++ b/viewvc-install @@ -1,4 +1,4 @@ -#!/usr/bin/python +#!/usr/bin/env python # -*- Mode: python -*- # # Copyright (C) 1999-2013 The ViewCVS Group. All Rights Reserved. @@ -60,6 +60,8 @@ FILE_INFO_LIST = [ ("bin/cvsdbadmin", "bin/cvsdbadmin", 0755, 1, 0, 0), ("bin/svndbadmin", "bin/svndbadmin", 0755, 1, 0, 0), ("bin/make-database", "bin/make-database", 0755, 1, 0, 0), + ("bin/svnupdate-async", "bin/svnupdate-async", 0755, 1, 0, 0), + ("bin/svnupdate-async.sh", "bin/svnupdate-async.sh", 0755, 1, 0, 0), ("conf/viewvc.conf.dist", "viewvc.conf.dist", 0644, 0, 0, 0), ("conf/viewvc.conf.dist", "viewvc.conf", 0644, 0, 1, 0), ("conf/cvsgraph.conf.dist", "cvsgraph.conf.dist", 0644, 0, 0, 0), @@ -235,492 +237,6 @@ LEGEND ### If we get here, we're creating or overwriting the existing file. - # Read the source file's contents. - try: - contents = open(src_path, "rb").read() - except IOError, e: - error(str(e)) - - # Ensure the existence of the containing directories. - dst_parent = os.path.dirname(destdir_path) - if not os.path.exists(dst_parent): - try: - compat.makedirs(dst_parent) - print " created %s%s" % (dst_parent, os.sep) - except os.error, e: - if e.errno == 17: # EEXIST: file exists - return - if e.errno == 13: # EACCES: permission denied - error("You do not have permission to create directory %s" \ - % (dst_parent)) - error("Unknown error creating directory %s" \ - % (dst_parent, OSError, e)) - - # Now, write the file contents to their destination. - try: - exists = os.path.exists(destdir_path) - open(destdir_path, "wb").write(contents) - print " %s %s" \ - % (exists and 'replaced ' or 'installed', dst_path) - except IOError, e: - if e.errno == 13: - # EACCES: permission denied - error("You do not have permission to write file %s" % (dst_path)) - error("Unknown error writing file %s" % (dst_path, IOError, e)) - - # Set the files's mode. - os.chmod(destdir_path, mode) - - # (Optionally) compile the file. - if compile_it: - py_compile.compile(destdir_path, destdir_path + "c" , dst_path) - - -def install_tree(src_path, dst_path, prompt_replace): - """Install a tree whose source is at SRC_PATH (which is relative - to the ViewVC source directory) into the location DST_PATH (which - is relative both to the global ROOT_DIR and DESTDIR settings). If - PROMPT_REPLACE is set (and is not overridden by global setting - CLEAN_MODE), prompt the user for how to deal with already existing - files that differ from the to-be-installed version.""" - - orig_src_path = src_path - orig_dst_path = dst_path - src_path = _actual_src_path(src_path) - dst_path = os.path.join(ROOT_DIR, string.replace(dst_path, '/', os.sep)) - destdir_path = os.path.join(DESTDIR + dst_path) - - # Get a list of items in the directory. - files = os.listdir(src_path) - files.sort() - for fname in files: - # Ignore some stuff found in development directories, but not - # intended for installation. - if fname == 'CVS' or fname == '.svn' or fname == '_svn' \ - or fname[-4:] == '.pyc' or fname[-5:] == '.orig' \ - or fname[-4:] == '.rej' or fname[0] == '.' \ - or fname[-1] == '~': - continue - - orig_src_child = orig_src_path + '/' + fname - orig_dst_child = orig_dst_path + '/' + fname - - # If the item is a subdirectory, recurse. Otherwise, install the file. - if os.path.isdir(os.path.join(src_path, fname)): - install_tree(orig_src_child, orig_dst_child, prompt_replace) - else: - set_paths = 0 - compile_it = fname[-3:] == '.py' - install_file(orig_src_child, orig_dst_child, 0644, - set_paths, prompt_replace, compile_it) - - # Check for .py and .pyc files that don't belong in installation. - for fname in os.listdir(destdir_path): - if not os.path.isfile(os.path.join(destdir_path, fname)) or \ - not ((fname[-3:] == '.py' and fname not in files) or - (fname[-4:] == '.pyc' and fname[:-1] not in files)): - continue - - # If we get here, there's cruft. - delete = None - if CLEAN_MODE == 'true': - delete = 1 - elif CLEAN_MODE == 'false': - delete = 0 - else: - print "File %s does not belong in ViewVC %s." \ - % (dst_path, version) - while 1: - temp = raw_input("Do you want to [D]elete it, or [L]eave " - "it as is? ") - temp = string.lower(temp[0]) - if temp == "l": - delete = 0 - elif temp == "d": - delete = 1 - - if delete is not None: - break - - assert delete is not None - if delete: - print " deleted %s" % (os.path.join(dst_path, fname)) - os.unlink(os.path.join(destdir_path, fname)) - else: - print " preserved %s" % (os.path.join(dst_path, fname)) - - - -def usage_and_exit(errstr=None): - stream = errstr and sys.stderr or sys.stdout - stream.write("""Usage: %s [OPTIONS] - -Installs the ViewVC web-based version control repository browser. - -Options: - - --help, -h, -? Show this usage message and exit. - - --prefix=DIR Install ViewVC into the directory DIR. If not provided, - the script will prompt for this information. - - --destdir=DIR Use DIR as the DESTDIR. This is generally only used - by package maintainers. If not provided, the script will - prompt for this information. - - --clean-mode= If 'true', overwrite existing ViewVC configuration files - found in the target directory, and purge Python modules - from the target directory that aren't part of the ViewVC - distribution. If 'false', do not overwrite configuration - files, and do not purge any files from the target - directory. If not specified, the script will prompt - for the appropriate action on a per-file basis. - -""" % (os.path.basename(sys.argv[0]))) - if errstr: - stream.write("ERROR: %s\n\n" % (errstr)) - sys.exit(1) - else: - sys.exit(0) - - -if __name__ == "__main__": - # Option parsing. - try: - optlist, args = getopt.getopt(sys.argv[1:], "h?", - ['prefix=', - 'destdir=', - 'clean-mode=', - 'help']) - except getopt.GetoptError, e: - usage_and_exit(str(e)) - for opt, arg in optlist: - if opt == '--help' or opt == '-h' or opt == '-?': - usage_and_exit() - if opt == '--prefix': - ROOT_DIR = arg - if opt == '--destdir': - DESTDIR = arg - if opt == '--clean-mode': - arg = arg.lower() - if arg not in ('true', 'false'): - usage_and_exit("Invalid value for --overwrite parameter.") - CLEAN_MODE = arg - - # Print the header greeting. - print """This is the ViewVC %s installer. - -It will allow you to choose the install path for ViewVC. You will now -be asked some installation questions. Defaults are given in square brackets. -Just hit [Enter] if a default is okay. -""" % version - - # Prompt for ROOT_DIR if none provided. - if ROOT_DIR is None: - if sys.platform == "win32": - pf = os.getenv("ProgramFiles", "C:\\Program Files") - default = os.path.join(pf, "viewvc-" + version) - else: - default = "/usr/local/viewvc-" + version - temp = string.strip(raw_input("Installation path [%s]: " \ - % default)) - print - if len(temp): - ROOT_DIR = temp - else: - ROOT_DIR = default - - # Prompt for DESTDIR if none provided. - if DESTDIR is None: - default = '' - temp = string.strip(raw_input( - "DESTDIR path (generally only used by package " - "maintainers) [%s]: " \ - % default)) - print - if len(temp): - DESTDIR = temp - else: - DESTDIR = default - - # Install the files. - print "Installing ViewVC to %s%s:" \ - % (ROOT_DIR, DESTDIR and " (DESTDIR = %s)" % (DESTDIR) or "") - for args in FILE_INFO_LIST: - apply(install_file, args) - for args in TREE_LIST: - apply(install_tree, args) - - # Write LIBRARY_DIR and CONF_PATHNAME into viewvcinstall.py config file - viewvcinstallpath = """#!/usr/bin/python -LIBRARY_DIR = "%s" -CONF_PATHNAME = "%s" -""" % (os.path.join(ROOT_DIR, 'lib'), os.path.join(ROOT_DIR, 'viewvc.conf')) - open(os.path.join(ROOT_DIR, 'bin', 'viewvcinstallpath.py'),'wb').write(viewvcinstallpath) - if sys.platform != 'win32': - for i in ['cgi', 'mod_python']: - os.symlink(os.path.join(ROOT_DIR, 'bin', 'viewvcinstallpath.py'), os.path.join(ROOT_DIR, 'bin', i, 'viewvcinstallpath.py')) - else: - for i in ['asp', 'cgi', 'mod_python']: - open(os.path.join(ROOT_DIR, 'bin', i, 'viewvcinstallpath.py'),'wb').write(viewvcinstallpath) - - # Print some final thoughts. - print """ - -ViewVC file installation complete. - -Consult the INSTALL document for detailed information on completing the -installation and configuration of ViewVC on your system. Here's a brief -overview of the remaining steps: - - 1) Edit the %s file. - - 2) Either configure an existing web server to run - %s. - - Or, copy %s to an - already-configured cgi-bin directory. - - Or, use the standalone server provided by this distribution at - %s. -""" % (os.path.join(ROOT_DIR, 'viewvc.conf'), - os.path.join(ROOT_DIR, 'bin', 'cgi', 'viewvc.cgi'), - os.path.join(ROOT_DIR, 'bin', 'cgi', 'viewvc.cgi'), - os.path.join(ROOT_DIR, 'bin', 'standalone.py')) -#!/usr/bin/env python -# -*- Mode: python -*- -# -# Copyright (C) 1999-2007 The ViewCVS Group. All Rights Reserved. -# -# By using this file, you agree to the terms and conditions set forth in -# the LICENSE.html file which can be found at the top level of the ViewVC -# distribution or at http://viewvc.org/license-1.html. -# -# For more information, visit http://viewvc.org/ -# -# ----------------------------------------------------------------------- -# -# Install script for ViewVC -# -# ----------------------------------------------------------------------- - -import os -import sys -import string -import re -import traceback -import py_compile -import getopt -import StringIO - -# Get access to our library modules. -sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'lib')) - -import compat -import viewvc -import compat_ndiff -version = viewvc.__version__ - - -## Installer defaults. -DESTDIR = None -ROOT_DIR = None -CLEAN_MODE = None - - -## List of files for installation. -## tuple (source path, -## destination path, -## mode, -## boolean -- search-and-replace? -## boolean -- prompt before replacing? -## boolean -- compile?) -FILE_INFO_LIST = [ - ("bin/cgi/viewvc.cgi", "bin/cgi/viewvc.cgi", 0755, 1, 0, 0), - ("bin/cgi/query.cgi", "bin/cgi/query.cgi", 0755, 1, 0, 0), - ("bin/mod_python/viewvc.py", "bin/mod_python/viewvc.py", 0755, 1, 0, 0), - ("bin/mod_python/query.py", "bin/mod_python/query.py", 0755, 1, 0, 0), - ("bin/mod_python/handler.py", "bin/mod_python/handler.py", 0755, 1, 0, 0), - ("bin/mod_python/.htaccess", "bin/mod_python/.htaccess", 0755, 0, 0, 0), - ("bin/standalone.py", "bin/standalone.py", 0755, 1, 0, 0), - ("bin/loginfo-handler", "bin/loginfo-handler", 0755, 1, 0, 0), - ("bin/cvsdbadmin", "bin/cvsdbadmin", 0755, 1, 0, 0), - ("bin/svndbadmin", "bin/svndbadmin", 0755, 1, 0, 0), - ("bin/make-database", "bin/make-database", 0755, 1, 0, 0), - ("bin/svnupdate-async", "bin/svnupdate-async", 0755, 1, 0, 0), - ("bin/svnupdate-async.sh", "bin/svnupdate-async.sh", 0755, 1, 0, 0), - ("conf/viewvc.conf.dist", "viewvc.conf.dist", 0644, 0, 0, 0), - ("conf/viewvc.conf.dist", "viewvc.conf", 0644, 0, 1, 0), - ("conf/cvsgraph.conf.dist", "cvsgraph.conf.dist", 0644, 0, 0, 0), - ("conf/cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0), - ("conf/mimetypes.conf.dist", "mimetypes.conf.dist", 0644, 0, 0, 0), - ("conf/mimetypes.conf.dist", "mimetypes.conf", 0644, 0, 1, 0), - ] -if sys.platform == "win32": - FILE_INFO_LIST.extend([ - ("bin/asp/viewvc.asp", "bin/asp/viewvc.asp", 0755, 1, 0, 0), - ("bin/asp/query.asp", "bin/asp/query.asp", 0755, 1, 0, 0), - ]) - - -## List of directories for installation. -## type (source path, -## destination path, -## boolean -- prompt before replacing?) -TREE_LIST = [ - ("lib", "lib", 0), - ("templates", "templates", 1), - ("templates-contrib", "templates-contrib", 1), - ] - - -## List of file extensions we can't show diffs for. -BINARY_FILE_EXTS = [ - '.png', - '.gif', - '.jpg', - ] - - -def _escape(str): - """Callback function for re.sub(). - - re.escape() is no good because it blindly puts backslashes in - front of anything that is not a number or letter regardless of - whether the resulting sequence will be interpreted.""" - return string.replace(str, "\\", "\\\\") - - -def _actual_src_path(path): - """Return the real on-disk location of PATH, which is relative to - the ViewVC source directory.""" - return os.path.join(os.path.dirname(sys.argv[0]), - string.replace(path, '/', os.sep)) - - -def error(text, etype=None, evalue=None): - """Print error TEXT to stderr, pretty printing the optional - exception type and value (ETYPE and EVALUE, respective), and then - exit the program with an errorful code.""" - sys.stderr.write("\n[ERROR] %s\n" % (text)) - if etype: - traceback.print_exception(etype, evalue, None, file=sys.stderr) - sys.exit(1) - - -def replace_var(contents, var, value): - """Replace instances of the variable VAR as found in file CONTENTS - with VALUE.""" - pattern = re.compile('^' + var + r'\s*=\s*.*$', re.MULTILINE) - repl = '%s = r"%s"' % (var, os.path.join(ROOT_DIR, value)) - return re.sub(pattern, _escape(repl), contents) - - -def replace_paths(contents): - """Replace all ViewVC path placeholders found in file CONTENTS.""" - if contents[:2] == '#!': - shbang = '#!' + sys.executable - contents = re.sub('^#![^\n]*', _escape(shbang), contents) - contents = replace_var(contents, 'LIBRARY_DIR', 'lib') - contents = replace_var(contents, 'CONF_PATHNAME', 'viewvc.conf') - return contents - - -def install_file(src_path, dst_path, mode, subst_path_vars, - prompt_replace, compile_it): - """Install a single file whose source is at SRC_PATH (which is - relative to the ViewVC source directory) into the location - DST_PATH (which is relative both to the global ROOT_DIR and - DESTDIR settings), and set the file's MODE. If SUBST_PATH_VARS is - set, substitute path variables in the file's contents. If - PROMPT_REPLACE is set (and is not overridden by global setting - CLEAN_MODE), prompt the user for how to deal with already existing - files that differ from the to-be-installed version. If COMPILE_IT - is set, compile the file as a Python module.""" - - src_path = _actual_src_path(src_path) - dst_path = os.path.join(ROOT_DIR, string.replace(dst_path, '/', os.sep)) - destdir_path = DESTDIR + dst_path - - overwrite = None - if not (prompt_replace and os.path.exists(destdir_path)): - # If the file doesn't already exist, or we've been instructed to - # replace it without prompting, then drop in the new file and get - # outta here. - overwrite = 1 - else: - # If we're here, then the file already exists, and we've possibly - # got to prompt the user for what to do about that. - - # Collect ndiff output from ndiff - sys.stdout = StringIO.StringIO() - compat_ndiff.main([destdir_path, src_path]) - ndiff_output = sys.stdout.getvalue() - - # Return everything to normal - sys.stdout = sys.__stdout__ - - # Collect the '+ ' and '- ' lines. - diff_lines = [] - looking_at_diff_lines = 0 - for line in string.split(ndiff_output, '\n'): - # Print line if it is a difference line - if line[:2] == "+ " or line[:2] == "- " or line[:2] == "? ": - diff_lines.append(line) - looking_at_diff_lines = 1 - else: - # Compress lines that are the same to print one blank line - if looking_at_diff_lines: - diff_lines.append("") - looking_at_diff_lines = 0 - - # If there are no differences, we're done here. - if not diff_lines: - overwrite = 1 - else: - # If we get here, there are differences. - if CLEAN_MODE == 'true': - overwrite = 1 - elif CLEAN_MODE == 'false': - overwrite = 0 - else: - print "File %s exists and is different from source file." \ - % (destdir_path) - while 1: - name, ext = os.path.splitext(src_path) - if ext in BINARY_FILE_EXTS: - temp = raw_input("Do you want to [O]verwrite or " - "[D]o not overwrite? ") - else: - temp = raw_input("Do you want to [O]verwrite, [D]o " - "not overwrite, or [V]iew " - "differences? ") - temp = string.lower(temp[0]) - if temp == "v" and ext not in BINARY_FILE_EXTS: - print """ ----------------------------------------------------------------------------""" - print string.join(diff_lines, '\n') + '\n' - print """ -LEGEND - A leading '- ' indicates line to remove from installed file - A leading '+ ' indicates line to add to installed file - A leading '? ' shows intraline differences. ----------------------------------------------------------------------------""" - elif temp == "d": - overwrite = 0 - elif temp == "o": - overwrite = 1 - - if overwrite is not None: - break - - assert overwrite is not None - if not overwrite: - print " preserved %s" % (dst_path) - return - - ### If we get here, we're creating or overwriting the existing file. - # Read the source file's contents. try: contents = open(src_path, "rb").read() @@ -922,7 +438,7 @@ Just hit [Enter] if a default is okay. ROOT_DIR = temp else: ROOT_DIR = default - + # Prompt for DESTDIR if none provided. if DESTDIR is None: default = '' @@ -933,7 +449,7 @@ Just hit [Enter] if a default is okay. DESTDIR = temp else: DESTDIR = default - + # Install the files. print "Installing ViewVC to %s%s:" \ % (ROOT_DIR, DESTDIR and " (DESTDIR = %s)" % (DESTDIR) or "") @@ -941,7 +457,20 @@ Just hit [Enter] if a default is okay. apply(install_file, args) for args in TREE_LIST: apply(install_tree, args) - + + # Write LIBRARY_DIR and CONF_PATHNAME into viewvcinstall.py config file + viewvcinstallpath = """#!/usr/bin/python +LIBRARY_DIR = "%s" +CONF_PATHNAME = "%s" +""" % (os.path.join(ROOT_DIR, 'lib'), os.path.join(ROOT_DIR, 'viewvc.conf')) + open(os.path.join(ROOT_DIR, 'bin', 'viewvcinstallpath.py'),'wb').write(viewvcinstallpath) + if sys.platform != 'win32': + for i in ['cgi', 'mod_python']: + os.symlink(os.path.join(ROOT_DIR, 'bin', 'viewvcinstallpath.py'), os.path.join(ROOT_DIR, 'bin', i, 'viewvcinstallpath.py')) + else: + for i in ['asp', 'cgi', 'mod_python']: + open(os.path.join(ROOT_DIR, 'bin', i, 'viewvcinstallpath.py'),'wb').write(viewvcinstallpath) + # Print some final thoughts. print """
Search content:
Repository: +   Repository type:   + +
+ + + + +
Branch: - +
Subdirectory: - - (You can list multiple directories separated by commas.) +
+ (you can list multiple directories separated by commas)
File: - +
Revision number: +
+ (you can list multiple revision numbers separated by commas) +
Who: - +
Comment: - +
+
Show at most - changed files per commit. (Use 0 to show all files.) + changed files per commit.
+ (use 0 to show all files)