mirror of
https://github.com/vitalif/viewvc-4intranet
synced 2019-04-16 04:14:59 +03:00
Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d7d567f089 | ||
![]() |
514da91629 | ||
![]() |
d2c8bb9878 | ||
![]() |
fe274e9b9c | ||
![]() |
6603a67a91 | ||
![]() |
b50de1c92e | ||
![]() |
dfd8dc82f6 | ||
![]() |
1a942d1062 | ||
![]() |
289a78eb99 | ||
![]() |
2897763020 | ||
![]() |
47886423e5 | ||
![]() |
0890821839 | ||
![]() |
2903389d3e | ||
![]() |
5bdea369e8 | ||
![]() |
3699e8e0c7 | ||
![]() |
55c0b30bc0 | ||
![]() |
6fd321529a | ||
![]() |
531877db4a | ||
![]() |
51df003d8e | ||
![]() |
7d05859d7e | ||
![]() |
2e48cd2ba1 | ||
![]() |
267b61f347 | ||
![]() |
7f53d5cc3c | ||
![]() |
af591ed9f7 | ||
![]() |
17492a3856 |
15
CHANGES
15
CHANGES
@@ -1,3 +1,18 @@
|
||||
Version 1.1.7 (released 09-Sep-2010)
|
||||
|
||||
* display Subversion revision properties in the revision view (issue #453)
|
||||
* fix exception in 'standalone.py -r REPOS' when run without a config file
|
||||
* fix standalone.py server root deployments (--script-alias='')
|
||||
* add rudimentary Basic authentication support to standalone.py (issue #49)
|
||||
* fix obscure "unexpected NULL parent pool" Subversion bindings error
|
||||
* enable path info / link display in remote Subversion root revision view
|
||||
* fix vhost name case handling inconsistency (issue #466)
|
||||
* use svn:mime-type property charset param as encoding hint
|
||||
* markup Subversion revision references in log messages (issue #313)
|
||||
* add rudimentary support for FastCGI-based deployments (issue #464)
|
||||
* fix query script WSGI deployment
|
||||
* add configuration to fix query script cross-linking to ViewVC
|
||||
|
||||
Version 1.1.6 (released 02-Jun-2010)
|
||||
|
||||
* add rudimentary support for WSGI-based deployments (issue #397)
|
||||
|
27
INSTALL
27
INSTALL
@@ -251,6 +251,33 @@ APACHE CONFIGURATION
|
||||
Note: WSGI support in ViewVC is at this time quite rudimentary,
|
||||
bordering on downright experimental. Your mileage may vary.
|
||||
|
||||
-----------------------------------------
|
||||
METHOD F: Using mod_fcgid (if installed)
|
||||
-----------------------------------------
|
||||
|
||||
This uses ViewVC's WSGI support (from above), but supports using FastCGI,
|
||||
and is a somewhat hybrid approach of several of the above methods.
|
||||
|
||||
Especially if fcgi is already being used for other purposes, e.g. PHP,
|
||||
also using fcgi can prevent the need for including additional modules
|
||||
(e.g. mod_python or mod_wsgi) within Apache, which may help lessen Apache's
|
||||
memory usage and/or help improve performance.
|
||||
|
||||
This depends on mod_fcgid:
|
||||
|
||||
http://httpd.apache.org/mod_fcgid/
|
||||
|
||||
as well as the fcgi server from Python's flup package:
|
||||
|
||||
http://pypi.python.org/pypi/flup
|
||||
http://trac.saddi.com/flup
|
||||
|
||||
The following are some example httpd.conf fragments you can use to
|
||||
support this configuration:
|
||||
|
||||
ScriptAlias /viewvc /usr/local/viewvc/bin/wsgi/viewvc.fcgi
|
||||
ScriptAlias /query /usr/local/viewvc/bin/wsgi/query.fcgi
|
||||
|
||||
3) Restart Apache.
|
||||
|
||||
The commands to do this vary. "httpd -k restart" and "apache -k
|
||||
|
@@ -54,7 +54,10 @@ import query
|
||||
server = sapi.AspServer(Server, Request, Response, Application)
|
||||
try:
|
||||
cfg = viewvc.load_config(CONF_PATHNAME, server)
|
||||
query.main(server, cfg, "viewvc.asp")
|
||||
viewvc_base_url = cfg.query.viewvc_base_url
|
||||
if viewvc_base_url is None:
|
||||
viewvc_base_url = "viewvc.asp"
|
||||
query.main(server, cfg, viewvc_base_url)
|
||||
finally:
|
||||
s.close()
|
||||
|
||||
|
@@ -54,4 +54,7 @@ import query
|
||||
|
||||
server = sapi.CgiServer()
|
||||
cfg = viewvc.load_config(CONF_PATHNAME, server)
|
||||
query.main(server, cfg, "viewvc.cgi")
|
||||
viewvc_base_url = cfg.query.viewvc_base_url
|
||||
if viewvc_base_url is None:
|
||||
viewvc_base_url = "viewvc.cgi"
|
||||
query.main(server, cfg, viewvc_base_url)
|
||||
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2008 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2010 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
|
||||
@@ -65,7 +65,10 @@ cfg = viewvc.load_config(CONF_PATHNAME)
|
||||
def index(req):
|
||||
server = sapi.ModPythonServer(req)
|
||||
try:
|
||||
query.main(server, cfg, "viewvc.py")
|
||||
viewvc_base_url = cfg.query.viewvc_base_url
|
||||
if viewvc_base_url is None:
|
||||
viewvc_base_url = "viewvc.py"
|
||||
query.main(server, cfg, viewvc_base_url)
|
||||
finally:
|
||||
server.close()
|
||||
|
||||
|
1166
bin/standalone.py
1166
bin/standalone.py
File diff suppressed because it is too large
Load Diff
54
bin/wsgi/query.fcgi
Normal file
54
bin/wsgi/query.fcgi
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# viewvc: View CVS/SVN repositories via a web browser
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# This is a fcgi entry point for the query ViewVC app. It's appropriate
|
||||
# for use with mod_fcgid and flup. It defines a single application function
|
||||
# that is a valid fcgi entry point.
|
||||
#
|
||||
# mod_fcgid: http://httpd.apache.org/mod_fcgid/
|
||||
# flup:
|
||||
# http://pypi.python.org/pypi/flup
|
||||
# http://trac.saddi.com/flup
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import sys, os
|
||||
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
if LIBRARY_DIR:
|
||||
sys.path.insert(0, LIBRARY_DIR)
|
||||
else:
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
|
||||
"../../../lib")))
|
||||
|
||||
import sapi
|
||||
import viewvc
|
||||
import query
|
||||
from flup.server import fcgi
|
||||
|
||||
def application(environ, start_response):
|
||||
server = sapi.WsgiServer(environ, start_response)
|
||||
cfg = viewvc.load_config(CONF_PATHNAME, server)
|
||||
viewvc_base_url = cfg.query.viewvc_base_url
|
||||
if viewvc_base_url is None:
|
||||
viewvc_base_url = "viewvc.fcgi"
|
||||
query.main(server, cfg, viewvc_base_url)
|
||||
return []
|
||||
|
||||
fcgi.WSGIServer(application).run()
|
@@ -1,6 +1,6 @@
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2009 The ViewCVS Group. All Rights Reserved.
|
||||
# Copyright (C) 1999-2010 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
|
||||
@@ -16,7 +16,7 @@
|
||||
#
|
||||
# This is a wsgi entry point for the query ViewVC app. It's appropriate
|
||||
# for use with mod_wsgi. It defines a single application function that
|
||||
# is a walid wsgi entry point.
|
||||
# is a valid wsgi entry point.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
@@ -38,5 +38,8 @@ import query
|
||||
def application(environ, start_response):
|
||||
server = sapi.WsgiServer(environ, start_response)
|
||||
cfg = viewvc.load_config(CONF_PATHNAME, server)
|
||||
query.main(server, cfg, "viewvc.wsgi")
|
||||
viewvc_base_url = cfg.query.viewvc_base_url
|
||||
if viewvc_base_url is None:
|
||||
viewvc_base_url = "viewvc.wsgi"
|
||||
query.main(server, cfg, viewvc_base_url)
|
||||
return []
|
||||
|
50
bin/wsgi/viewvc.fcgi
Normal file
50
bin/wsgi/viewvc.fcgi
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/python
|
||||
# -*-python-*-
|
||||
#
|
||||
# Copyright (C) 1999-2010 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/
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# viewvc: View CVS/SVN repositories via a web browser
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
#
|
||||
# This is a fcgi entry point for the main ViewVC app. It's appropriate
|
||||
# for use with mod_fcgid and flup. It defines a single application function
|
||||
# that is a valid fcgi entry point.
|
||||
#
|
||||
# mod_fcgid: http://httpd.apache.org/mod_fcgid/
|
||||
# flup:
|
||||
# http://pypi.python.org/pypi/flup
|
||||
# http://trac.saddi.com/flup
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
import sys, os
|
||||
|
||||
LIBRARY_DIR = None
|
||||
CONF_PATHNAME = None
|
||||
|
||||
if LIBRARY_DIR:
|
||||
sys.path.insert(0, LIBRARY_DIR)
|
||||
else:
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(sys.argv[0],
|
||||
"../../../lib")))
|
||||
|
||||
import sapi
|
||||
import viewvc
|
||||
from flup.server import fcgi
|
||||
|
||||
def application(environ, start_response):
|
||||
server = sapi.WsgiServer(environ, start_response)
|
||||
cfg = viewvc.load_config(CONF_PATHNAME, server)
|
||||
viewvc.main(server, cfg)
|
||||
return []
|
||||
|
||||
fcgi.WSGIServer(application).run()
|
@@ -16,7 +16,7 @@
|
||||
#
|
||||
# This is a wsgi entry point for the main ViewVC app. It's appropriate
|
||||
# for use with mod_wsgi. It defines a single application function that
|
||||
# is a walid wsgi entry point.
|
||||
# is a valid wsgi entry point.
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
|
@@ -106,27 +106,47 @@
|
||||
##
|
||||
#svn_roots =
|
||||
|
||||
## root_parents: Specifies a list of directories in which any number of
|
||||
## repositories may reside. Rather than force you to add a new entry
|
||||
## to 'cvs_roots' or 'svn_roots' each time you create a new repository,
|
||||
## ViewVC rewards you for organising all your repositories under a few
|
||||
## parent directories by allowing you to simply specifiy just those
|
||||
## parent directories. ViewVC will then notice each repository in that
|
||||
## directory as a new root whose name is the subdirectory of the parent
|
||||
## path in which that repository lives.
|
||||
## root_parents: Specifies a list of directories under which any
|
||||
## number of repositories may reside. You can specify multiple root
|
||||
## parents separated by commas or new lines, each of which is of the
|
||||
## form "path: type" (where type is either "cvs" or "svn").
|
||||
##
|
||||
## You can specify multiple parent paths separated by commas or new lines.
|
||||
## Rather than force you to add a new entry to 'cvs_roots' or
|
||||
## 'svn_roots' each time you create a new repository, ViewVC rewards
|
||||
## you for organizing all your repositories under a few parent
|
||||
## directories by allowing you to simply tell it about those parent
|
||||
## directories. ViewVC will then notice each repository of the
|
||||
## specified type in that directory as a root whose name is the
|
||||
## subdirectory in which that repository lives.
|
||||
##
|
||||
## WARNING: these names can, of course, clash with names you have
|
||||
## defined in your cvs_roots or svn_roots configuration items. If this
|
||||
## occurs, you can either rename the offending repository on disk, or
|
||||
## grant new names to the clashing item in cvs_roots or svn_roots.
|
||||
## Each parent path is processed sequentially, so repositories under
|
||||
## later parent paths may override earlier ones.
|
||||
## For example, if you have three Subversion repositories located at
|
||||
## /opt/svn/projects, /opt/svn/websites, and /opt/svn/devstuff, you
|
||||
## could list them individually in svn_roots like so:
|
||||
##
|
||||
## svn_roots = projects: /opt/svn/projects,
|
||||
## websites: /opt/svn/websites,
|
||||
## devstuff: /opt/svn/devstuff
|
||||
##
|
||||
## or you could instead use the root_parents configuration option:
|
||||
##
|
||||
## root_parents = /opt/svn: svn
|
||||
##
|
||||
## The benefit of this latter approach is that, as you add new
|
||||
## repositories to your /opt/svn directory, they automatically become
|
||||
## available for display in ViewVC without additional configuration.
|
||||
##
|
||||
## WARNING: the root names derived for repositories configured via the
|
||||
## root_parents option can, of course, clash with names you have
|
||||
## defined in your cvs_roots or svn_roots configuration items. If
|
||||
## this occurs, you can either rename the offending repository on
|
||||
## disk, or grant new names to the clashing item in cvs_roots or
|
||||
## svn_roots. Each parent path is processed sequentially, so the
|
||||
## names of repositories under later parent paths may override earlier
|
||||
## ones.
|
||||
##
|
||||
## Example:
|
||||
## root_parents = /opt/svn : svn,
|
||||
## /opt/cvs : cvs
|
||||
## root_parents = /opt/svn: svn,
|
||||
## /opt/cvs: cvs
|
||||
##
|
||||
#root_parents =
|
||||
|
||||
@@ -997,3 +1017,30 @@
|
||||
#force_username_case =
|
||||
|
||||
##---------------------------------------------------------------------------
|
||||
[query]
|
||||
|
||||
## The configuration items in this section are used exclusively by the
|
||||
## 'query' script, a separate script from ViewVC itself that ships
|
||||
## with ViewVC and allows for queries into the ViewVC commits
|
||||
## database. If you aren't using this separate script (which was made
|
||||
## largely irrelevant by the introduction of an integrated "query"
|
||||
## view in ViewVC itself, or aren't using the ViewVC commits database
|
||||
## functionality at all, you can ignore these configurations items
|
||||
## altogether.
|
||||
|
||||
## viewvc_base_url: Base URL at which ViewVC may be accessed on this
|
||||
## server. The default value for this option is determined at
|
||||
## run-time by the various front-ends to the query script.
|
||||
##
|
||||
## Examples:
|
||||
## viewvc_base_url = /viewvc.py
|
||||
## viewvc_base_url = /viewvc.wsgi
|
||||
## viewvc_base_url = /cgi-bin/viewvc
|
||||
## viewvc_base_url = viewvc
|
||||
##
|
||||
## To disable cross-linking between the query script and ViewVC,
|
||||
## uncomment this option and leave its value empty.
|
||||
##
|
||||
#viewvc_base_url =
|
||||
|
||||
##---------------------------------------------------------------------------
|
||||
|
@@ -88,6 +88,11 @@ import fnmatch
|
||||
# | vhosts |
|
||||
# | |
|
||||
# `-----------'
|
||||
# ,-----------.
|
||||
# | |
|
||||
# | query |
|
||||
# | |
|
||||
# `-----------'
|
||||
#
|
||||
# ### TODO: Figure out what this all means for the 'kv' stuff.
|
||||
#
|
||||
@@ -100,6 +105,7 @@ class Config:
|
||||
'cvsdb',
|
||||
'general',
|
||||
'options',
|
||||
'query',
|
||||
'templates',
|
||||
'utilities',
|
||||
)
|
||||
@@ -145,6 +151,7 @@ class Config:
|
||||
self.conf_path = os.path.isfile(pathname) and pathname or None
|
||||
self.base = os.path.dirname(pathname)
|
||||
self.parser = ConfigParser.ConfigParser()
|
||||
self.parser.optionxform = lambda x: x # don't case-normalize option names.
|
||||
self.parser.read(self.conf_path or [])
|
||||
|
||||
for section in self.parser.sections():
|
||||
@@ -172,6 +179,7 @@ class Config:
|
||||
fname = string.replace(fname, '%lang%', language)
|
||||
|
||||
parser = ConfigParser.ConfigParser()
|
||||
parser.optionxform = lambda x: x # don't case-normalize option names.
|
||||
parser.read(os.path.join(self.base, fname))
|
||||
for section in parser.sections():
|
||||
for option in parser.options(section):
|
||||
@@ -322,7 +330,7 @@ class Config:
|
||||
assert(self.root_options_overlayed == 0)
|
||||
|
||||
if not self.conf_path:
|
||||
return None
|
||||
return None, {}
|
||||
|
||||
# Figure out the authorizer by searching first for a per-root
|
||||
# override, then falling back to the base/vhost configuration.
|
||||
@@ -453,6 +461,8 @@ class Config:
|
||||
self.cvsdb.rss_row_limit = 100
|
||||
self.cvsdb.check_database_for_root = 0
|
||||
|
||||
self.query.viewvc_base_url = None
|
||||
|
||||
def _startswith(somestr, substr):
|
||||
return somestr[:len(substr)] == substr
|
||||
|
||||
|
23
lib/query.py
23
lib/query.py
@@ -312,11 +312,7 @@ def is_forbidden(cfg, cvsroot_name, module):
|
||||
|
||||
def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
|
||||
ob = _item(num_files=len(files), files=[])
|
||||
|
||||
if desc:
|
||||
ob.log = string.replace(server.escape(desc), '\n', '<br />')
|
||||
else:
|
||||
ob.log = ' '
|
||||
ob.log = desc and string.replace(server.escape(desc), '\n', '<br />') or ''
|
||||
|
||||
for commit in files:
|
||||
repository = commit.GetRepository()
|
||||
@@ -350,9 +346,10 @@ def build_commit(server, cfg, desc, files, cvsroots, viewvc_link):
|
||||
except:
|
||||
raise Exception, str([directory, commit.GetFile()])
|
||||
|
||||
## if we couldn't find the cvsroot path configured in the
|
||||
## viewvc.conf file, then don't make the link
|
||||
if cvsroot_name:
|
||||
## If we couldn't find the cvsroot path configured in the
|
||||
## viewvc.conf file, or we don't have a VIEWVC_LINK, then
|
||||
## don't make the link.
|
||||
if cvsroot_name and viewvc_link:
|
||||
flink = '[%s] <a href="%s/%s?root=%s">%s</a>' % (
|
||||
cvsroot_name, viewvc_link, urllib.quote(file),
|
||||
cvsroot_name, file)
|
||||
@@ -435,6 +432,10 @@ def main(server, cfg, viewvc_link):
|
||||
commits = [ ]
|
||||
query = 'skipped'
|
||||
|
||||
docroot = cfg.options.docroot
|
||||
if docroot is None and viewvc_link:
|
||||
docroot = viewvc_link + '/' + viewvc.docroot_magic_path
|
||||
|
||||
data = ezt.TemplateData({
|
||||
'cfg' : cfg,
|
||||
'address' : cfg.general.address,
|
||||
@@ -444,13 +445,9 @@ def main(server, cfg, viewvc_link):
|
||||
'directory' : server.escape(form_data.directory),
|
||||
'file' : server.escape(form_data.file),
|
||||
'who' : server.escape(form_data.who),
|
||||
'docroot' : cfg.options.docroot is None \
|
||||
and viewvc_link + '/' + viewvc.docroot_magic_path \
|
||||
or cfg.options.docroot,
|
||||
|
||||
'docroot' : docroot,
|
||||
'sortby' : form_data.sortby,
|
||||
'date' : form_data.date,
|
||||
|
||||
'query' : query,
|
||||
'commits' : commits,
|
||||
'num_commits' : len(commits),
|
||||
|
@@ -186,7 +186,7 @@ class CgiServer(Server):
|
||||
def FieldStorage(fp=None, headers=None, outerboundary="",
|
||||
environ=os.environ, keep_blank_values=0, strict_parsing=0):
|
||||
return cgi.FieldStorage(fp, headers, outerboundary, environ,
|
||||
keep_blank_values, strict_parsing)
|
||||
keep_blank_values, strict_parsing)
|
||||
|
||||
def write(self, s):
|
||||
sys.stdout.write(s)
|
||||
@@ -244,7 +244,7 @@ class WsgiServer(Server):
|
||||
|
||||
def FieldStorage(self, fp=None, headers=None, outerboundary="",
|
||||
environ=os.environ, keep_blank_values=0, strict_parsing=0):
|
||||
return cgi.FieldStorage(self._environ["wsgi.input"], self._headers,
|
||||
return cgi.FieldStorage(self._environ["wsgi.input"], headers,
|
||||
outerboundary, self._environ, keep_blank_values,
|
||||
strict_parsing)
|
||||
|
||||
|
@@ -180,8 +180,10 @@ class Repository:
|
||||
|
||||
rev is the revision of the item to return information about
|
||||
|
||||
Return value is a 4-tuple containing the date, author, log
|
||||
message, and a list of ChangedPath items representing paths changed
|
||||
Return value is a 5-tuple containing: the date, author, log
|
||||
message, a list of ChangedPath items representing paths changed,
|
||||
and a dictionary mapping property names to property values for
|
||||
properties stored on an item.
|
||||
|
||||
Raise vclib.UnsupportedFeature if the version control system
|
||||
doesn't support a global revision concept.
|
||||
|
@@ -18,10 +18,9 @@ import os
|
||||
import string
|
||||
import re
|
||||
import tempfile
|
||||
import popen2
|
||||
import time
|
||||
import urllib
|
||||
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev, _fix_subversion_exception
|
||||
from svn_repos import Revision, SVNChangedPath, _datestr_to_date, _compare_paths, _path_parts, _cleanup_path, _rev2optrev, _fix_subversion_exception, _split_revprops
|
||||
from svn import core, delta, client, wc, ra
|
||||
|
||||
|
||||
@@ -52,6 +51,29 @@ 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):
|
||||
try:
|
||||
client.svn_client_log4([url], start_rev, start_rev, end_rev,
|
||||
log_limit, 1, not cross_copies, 0, None,
|
||||
cb_func, ctx)
|
||||
except NameError:
|
||||
# Wrap old svn_log_message_receiver_t interface with a
|
||||
# svn_log_entry_t one.
|
||||
def cb_convert(paths, revision, author, date, message, pool):
|
||||
class svn_log_entry_t:
|
||||
pass
|
||||
log_entry = svn_log_entry_t()
|
||||
log_entry.changed_paths = paths
|
||||
log_entry.revision = revision
|
||||
log_entry.revprops = { core.SVN_PROP_REVISION_LOG : message,
|
||||
core.SVN_PROP_REVISION_AUTHOR : author,
|
||||
core.SVN_PROP_REVISION_DATE : date,
|
||||
}
|
||||
cb_func(log_entry, pool)
|
||||
client.svn_client_log2([url], start_rev, end_rev, log_limit,
|
||||
1, not cross_copies, cb_convert, ctx)
|
||||
|
||||
### END COMPATABILITY CODE ###
|
||||
|
||||
|
||||
@@ -68,7 +90,11 @@ class LogCollector:
|
||||
self.show_all_logs = show_all_logs
|
||||
self.lockinfo = lockinfo
|
||||
|
||||
def add_log(self, paths, revision, author, date, message, pool):
|
||||
def add_log(self, log_entry, pool):
|
||||
paths = log_entry.changed_paths
|
||||
revision = log_entry.revision
|
||||
msg, author, date, revprops = _split_revprops(log_entry.revprops)
|
||||
|
||||
# Changed paths have leading slashes
|
||||
changed_paths = paths.keys()
|
||||
changed_paths.sort(lambda a, b: _compare_paths(a, b))
|
||||
@@ -88,8 +114,8 @@ 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, _datestr_to_date(date), author, message, None,
|
||||
self.lockinfo, self.path[1:], None, None)
|
||||
entry = Revision(revision, date, author, msg, None, self.lockinfo,
|
||||
self.path[1:], None, None)
|
||||
self.logs.append(entry)
|
||||
if this_path:
|
||||
self.path = this_path
|
||||
@@ -157,7 +183,7 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
# Setup the client context baton, complete with non-prompting authstuffs.
|
||||
# TODO: svn_cmdline_setup_auth_baton() is mo' better (when available)
|
||||
core.svn_config_ensure(self.config_dir)
|
||||
self.ctx = client.svn_client_ctx_t()
|
||||
self.ctx = client.svn_client_create_context()
|
||||
self.ctx.auth_baton = core.svn_auth_open([
|
||||
client.svn_client_get_simple_provider(),
|
||||
client.svn_client_get_username_provider(),
|
||||
@@ -252,7 +278,7 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
if not vclib.check_path_access(self, entry_path_parts, entry.kind, rev):
|
||||
continue
|
||||
dirent = dirents[entry.name]
|
||||
entry.date, entry.author, entry.log, changes = \
|
||||
entry.date, entry.author, entry.log, revprops, changes = \
|
||||
self.revinfo(dirent.created_rev)
|
||||
entry.rev = str(dirent.created_rev)
|
||||
entry.size = dirent.size
|
||||
@@ -283,9 +309,8 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
log_limit = 0
|
||||
if limit:
|
||||
log_limit = first + limit
|
||||
client.svn_client_log2([url], _rev2optrev(rev), _rev2optrev(1),
|
||||
log_limit, 1, not cross_copies,
|
||||
lc.add_log, self.ctx)
|
||||
client_log(url, _rev2optrev(rev), _rev2optrev(1), log_limit,
|
||||
cross_copies, lc.add_log, self.ctx)
|
||||
revs = lc.logs
|
||||
revs.sort()
|
||||
prev = None
|
||||
@@ -337,7 +362,7 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
if not cached_info:
|
||||
cached_info = self._revinfo_raw(rev)
|
||||
self._revinfo_cache[rev] = cached_info
|
||||
return cached_info[0], cached_info[1], cached_info[2], cached_info[3]
|
||||
return tuple(cached_info)
|
||||
|
||||
def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
|
||||
p1 = self._getpath(path_parts1)
|
||||
@@ -352,7 +377,7 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
args = vclib._diff_args(type, options)
|
||||
|
||||
def _date_from_rev(rev):
|
||||
date, author, msg, changes = self.revinfo(rev)
|
||||
date, author, msg, revprops, changes = self.revinfo(rev)
|
||||
return date
|
||||
|
||||
try:
|
||||
@@ -417,29 +442,54 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
return revisions[0]
|
||||
|
||||
def _revinfo_raw(self, rev):
|
||||
# return 5-tuple (date, author, message, changes)
|
||||
# return 5-tuple (date, author, msg, revprops, changes)
|
||||
optrev = _rev2optrev(rev)
|
||||
revs = []
|
||||
|
||||
def _log_cb(changed_paths, revision, author,
|
||||
datestr, message, pool, retval=revs):
|
||||
date = _datestr_to_date(datestr)
|
||||
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))
|
||||
revision = log_entry.revision
|
||||
msg, author, date, revprops = _split_revprops(log_entry.revprops)
|
||||
action_map = { 'D' : vclib.DELETED,
|
||||
'A' : vclib.ADDED,
|
||||
'R' : vclib.REPLACED,
|
||||
'M' : vclib.MODIFIED,
|
||||
}
|
||||
paths = (changed_paths or {}).keys()
|
||||
paths.sort(lambda a, b: _compare_paths(a, b))
|
||||
changes = []
|
||||
found_readable = found_unreadable = 0
|
||||
for path in paths:
|
||||
pathtype = None
|
||||
change = changed_paths[path]
|
||||
action = action_map.get(change.action, vclib.MODIFIED)
|
||||
### 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.
|
||||
text_modified = props_modified = 0
|
||||
if hasattr(change, 'text_modified'):
|
||||
if change.text_modified == core.svn_tristate_true:
|
||||
text_modified = 1
|
||||
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?"
|
||||
action = action_map.get(change.action, vclib.MODIFIED)
|
||||
if change.copyfrom_path and change.copyfrom_rev:
|
||||
is_copy = 1
|
||||
base_path = change.copyfrom_path
|
||||
@@ -462,21 +512,21 @@ class RemoteSubversionRepository(vclib.Repository):
|
||||
base_path = None
|
||||
base_rev = None
|
||||
changes.append(SVNChangedPath(path, revision, pathtype, base_path,
|
||||
base_rev, action, is_copy, 0, 0))
|
||||
base_rev, action, is_copy,
|
||||
text_modified, props_modified))
|
||||
found_readable = 1
|
||||
else:
|
||||
found_unreadable = 1
|
||||
|
||||
if found_unreadable:
|
||||
message = None
|
||||
msg = None
|
||||
if not found_readable:
|
||||
author = None
|
||||
date = None
|
||||
revs.append([date, author, message, changes])
|
||||
revs.append([date, author, msg, revprops, changes])
|
||||
|
||||
client.svn_client_log([self.rootpath], optrev, optrev,
|
||||
1, 0, _log_cb, self.ctx)
|
||||
return revs[0][0], revs[0][1], revs[0][2], revs[0][3]
|
||||
client_log(self.rootpath, optrev, optrev, 1, 0, _log_cb, self.ctx)
|
||||
return tuple(revs[0])
|
||||
|
||||
##--- custom --##
|
||||
|
||||
|
@@ -115,6 +115,27 @@ def _rootpath2url(rootpath, path):
|
||||
return 'file://' + string.join([rootpath, path], "/")
|
||||
|
||||
|
||||
# Given a dictionary REVPROPS of revision properties, pull special
|
||||
# ones out of them and return a 4-tuple containing the log message,
|
||||
# the author, the date (converted from the date string property), and
|
||||
# a dictionary of any/all other revprops.
|
||||
def _split_revprops(revprops):
|
||||
if not revprops:
|
||||
return None, None, None, {}
|
||||
special_props = []
|
||||
for prop in core.SVN_PROP_REVISION_LOG, \
|
||||
core.SVN_PROP_REVISION_AUTHOR, \
|
||||
core.SVN_PROP_REVISION_DATE:
|
||||
if revprops.has_key(prop):
|
||||
special_props.append(revprops[prop])
|
||||
del(revprops[prop])
|
||||
else:
|
||||
special_props.append(None)
|
||||
msg, author, datestr = tuple(special_props)
|
||||
date = _datestr_to_date(datestr)
|
||||
return msg, author, date, revprops
|
||||
|
||||
|
||||
def _datestr_to_date(datestr):
|
||||
try:
|
||||
return core.svn_time_from_cstring(datestr) / 1000000
|
||||
@@ -225,7 +246,7 @@ def _log_helper(svnrepos, path, rev, lockinfo):
|
||||
copyfrom_rev, copyfrom_path = fs.copied_from(rev_root, path)
|
||||
|
||||
# Assemble our LogEntry
|
||||
date, author, msg, changes = svnrepos._revinfo(rev)
|
||||
date, author, msg, revprops, changes = svnrepos._revinfo(rev)
|
||||
if fs.is_file(rev_root, path):
|
||||
size = fs.file_length(rev_root, path)
|
||||
else:
|
||||
@@ -319,7 +340,7 @@ class BlameSource:
|
||||
self.first_rev = first_rev
|
||||
self.blame_data = []
|
||||
|
||||
ctx = client.ctx_t()
|
||||
ctx = client.svn_client_create_context()
|
||||
core.svn_config_ensure(config_dir)
|
||||
ctx.config = core.svn_config_get_config(config_dir)
|
||||
ctx.auth_baton = core.svn_auth_open([])
|
||||
@@ -472,7 +493,7 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
continue
|
||||
path = self._getpath(entry_path_parts)
|
||||
entry_rev = _get_last_history_rev(fsroot, path)
|
||||
date, author, msg, changes = self._revinfo(entry_rev)
|
||||
date, author, msg, revprops, changes = self._revinfo(entry_rev)
|
||||
entry.rev = str(entry_rev)
|
||||
entry.date = date
|
||||
entry.author = author
|
||||
@@ -577,16 +598,13 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
# Get the revision property info. (Would use
|
||||
# editor.get_root_props(), but something is broken there...)
|
||||
revprops = fs.revision_proplist(self.fs_ptr, rev)
|
||||
msg = revprops.get(core.SVN_PROP_REVISION_LOG)
|
||||
author = revprops.get(core.SVN_PROP_REVISION_AUTHOR)
|
||||
datestr = revprops.get(core.SVN_PROP_REVISION_DATE)
|
||||
date = _datestr_to_date(datestr)
|
||||
msg, author, date, revprops = _split_revprops(revprops)
|
||||
|
||||
# Optimization: If our caller doesn't care about the changed
|
||||
# paths, and we don't need them to do authz determinations, let's
|
||||
# get outta here.
|
||||
if self.auth is None and not include_changed_paths:
|
||||
return date, author, msg, None
|
||||
return date, author, msg, revprops, None
|
||||
|
||||
# If we get here, then we either need the changed paths because we
|
||||
# were asked for them, or we need them to do authorization checks.
|
||||
@@ -661,7 +679,7 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
# here for authz reasons only. That means the minute we've
|
||||
# found both a readable and an unreadable path, we can bail out.
|
||||
if (not include_changed_paths) and found_readable and found_unreadable:
|
||||
return date, author, None, None
|
||||
return date, author, None, None, None
|
||||
|
||||
# Okay, we've process all our paths. Let's filter our metadata,
|
||||
# and return the requested data.
|
||||
@@ -671,9 +689,9 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
author = None
|
||||
date = None
|
||||
if include_changed_paths:
|
||||
return date, author, msg, changedpaths.values()
|
||||
return date, author, msg, revprops, changedpaths.values()
|
||||
else:
|
||||
return date, author, msg, None
|
||||
return date, author, msg, revprops, None
|
||||
|
||||
# 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
|
||||
@@ -681,10 +699,10 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
rev = self._getrev(rev)
|
||||
cached_info = self._revinfo_cache.get(rev)
|
||||
if not cached_info \
|
||||
or (include_changed_paths and cached_info[3] is None):
|
||||
or (include_changed_paths and cached_info[4] is None):
|
||||
cached_info = _revinfo_helper(rev, include_changed_paths)
|
||||
self._revinfo_cache[rev] = cached_info
|
||||
return cached_info[0], cached_info[1], cached_info[2], cached_info[3]
|
||||
return tuple(cached_info)
|
||||
|
||||
def revinfo(self, rev):
|
||||
return self._revinfo(rev, 1)
|
||||
@@ -702,7 +720,7 @@ class LocalSubversionRepository(vclib.Repository):
|
||||
args = vclib._diff_args(type, options)
|
||||
|
||||
def _date_from_rev(rev):
|
||||
date, author, msg, changes = self._revinfo(rev)
|
||||
date, author, msg, revprops, changes = self._revinfo(rev)
|
||||
return date
|
||||
|
||||
try:
|
||||
|
168
lib/viewvc.py
168
lib/viewvc.py
@@ -14,7 +14,7 @@
|
||||
#
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
__version__ = '1.1.6-dev'
|
||||
__version__ = '1.1.7'
|
||||
|
||||
# this comes from our library; measure the startup time
|
||||
import debug
|
||||
@@ -273,7 +273,8 @@ class Request:
|
||||
self.roottype = 'cvs'
|
||||
else:
|
||||
raise debug.ViewVCException(
|
||||
'The root "%s" has an unknown type (%s).' % (self.rootname, type),
|
||||
'The root "%s" has an unknown type ("%s"). Expected "cvs" or "svn".'
|
||||
% (self.rootname, type),
|
||||
"500 Internal Server Error")
|
||||
|
||||
# If this is using an old-style 'rev' parameter, redirect to new hotness.
|
||||
@@ -896,14 +897,17 @@ def get_view_template(cfg, view_name, language="en"):
|
||||
|
||||
return template
|
||||
|
||||
def get_writeready_server_file(request, content_type=None):
|
||||
def get_writeready_server_file(request, content_type=None, encoding=None):
|
||||
"""Return a file handle to a response body stream, after outputting
|
||||
any queued special headers (on REQUEST.server) and (optionally) a
|
||||
'Content-Type' header whose value is CONTENT_TYPE. After this is
|
||||
called, it is too late to add new headers to the response."""
|
||||
'Content-Type' header whose value is CONTENT_TYPE and character set
|
||||
is ENCODING. After this is called, it is too late to add new
|
||||
headers to the response."""
|
||||
if request.gzip_compress_level:
|
||||
request.server.addheader('Content-Encoding', 'gzip')
|
||||
if content_type:
|
||||
if content_type and encoding:
|
||||
request.server.header("%s; charset=%s" % (content_type, encoding))
|
||||
elif content_type:
|
||||
request.server.header(content_type)
|
||||
else:
|
||||
request.server.header()
|
||||
@@ -1085,8 +1089,10 @@ _re_rewrite_url = re.compile('((http|https|ftp|file|svn|svn\+ssh)'
|
||||
_re_rewrite_email = re.compile('([-a-zA-Z0-9_.\+]+)@'
|
||||
'(([-a-zA-Z0-9]+\.)+[A-Za-z]{2,4})')
|
||||
|
||||
# Matches revision references
|
||||
_re_rewrite_svnrevref = re.compile(r'\b(r|rev #?|revision #?)([0-9]+)\b')
|
||||
|
||||
class HtmlFormatter:
|
||||
class ViewVCHtmlFormatter:
|
||||
"""Format a string as HTML-encoded output with customizable markup
|
||||
rules, for example turning strings that look like URLs into anchor links.
|
||||
|
||||
@@ -1152,6 +1158,25 @@ class HtmlFormatter:
|
||||
trunc_s = mobj.group(1)[:maxlen]
|
||||
return self._entity_encode(trunc_s), len(trunc_s)
|
||||
|
||||
def format_svnrevref(self, mobj, userdata, maxlen=0):
|
||||
"""Return a 2-tuple containing:
|
||||
- the text represented by MatchObject MOBJ, formatted as an
|
||||
linkified URL to a ViewVC Subversion revision view, 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.
|
||||
|
||||
USERDATA is a function that accepts a revision reference
|
||||
and returns a URL to that revision.
|
||||
"""
|
||||
s = mobj.group(0)
|
||||
revref = mobj.group(2)
|
||||
trunc_s = maxlen and s[:maxlen] or s
|
||||
revref_url = userdata(revref)
|
||||
return '<a href="%s">%s</a>' % (sapi.escape(revref_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
|
||||
@@ -1240,13 +1265,22 @@ class HtmlFormatter:
|
||||
s = ''
|
||||
return tokens
|
||||
|
||||
def format_log(log, cfg, maxlen=0, htmlize=1):
|
||||
|
||||
def format_log(request, log, maxlen=0, htmlize=1):
|
||||
if not log:
|
||||
return log
|
||||
|
||||
cfg = request.cfg
|
||||
if htmlize:
|
||||
lf = HtmlFormatter()
|
||||
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:
|
||||
@@ -1485,7 +1519,8 @@ class MarkupPipeWrapper:
|
||||
if self.posttext:
|
||||
ctx.fp.write(self.posttext)
|
||||
|
||||
def markup_stream_pygments(request, cfg, blame_data, fp, filename, mime_type):
|
||||
def markup_stream_pygments(request, cfg, blame_data, fp, filename,
|
||||
mime_type, encoding):
|
||||
# Determine if we should use Pygments to highlight our output.
|
||||
# Reasons not to include a) being told not to by the configuration,
|
||||
# b) not being able to import the Pygments modules, and c) Pygments
|
||||
@@ -1511,13 +1546,14 @@ def markup_stream_pygments(request, cfg, blame_data, fp, filename, mime_type):
|
||||
get_lexer_by_name, \
|
||||
get_lexer_for_mimetype, \
|
||||
get_lexer_for_filename
|
||||
encoding = 'guess'
|
||||
if cfg.options.detect_encoding:
|
||||
try:
|
||||
import chardet
|
||||
encoding = 'chardet'
|
||||
except (SyntaxError, ImportError):
|
||||
pass
|
||||
if not encoding:
|
||||
encoding = 'guess'
|
||||
if cfg.options.detect_encoding:
|
||||
try:
|
||||
import chardet
|
||||
encoding = 'chardet'
|
||||
except (SyntaxError, ImportError):
|
||||
pass
|
||||
try:
|
||||
lexer = get_lexer_for_mimetype(mime_type,
|
||||
encoding=encoding,
|
||||
@@ -1621,9 +1657,8 @@ def get_itemprops(request, path_parts, rev):
|
||||
propnames = itemprops.keys()
|
||||
propnames.sort()
|
||||
props = []
|
||||
has_binary_props = 0
|
||||
for name in propnames:
|
||||
value = format_log(itemprops[name], request.cfg)
|
||||
value = format_log(request, itemprops[name])
|
||||
undisplayable = ezt.boolean(0)
|
||||
# skip non-utf8 property names
|
||||
try:
|
||||
@@ -1639,20 +1674,33 @@ def get_itemprops(request, path_parts, rev):
|
||||
props.append(_item(name=name, value=value, undisplayable=undisplayable))
|
||||
return props
|
||||
|
||||
def parse_mime_type(mime_type):
|
||||
mime_parts = map(lambda x: x.strip(), string.split(mime_type, ';'))
|
||||
type_subtype = mime_parts[0].lower()
|
||||
parameters = {}
|
||||
for part in mime_parts[1:]:
|
||||
name, value = string.split(part, '=', 1)
|
||||
parameters[name] = value
|
||||
return type_subtype, parameters
|
||||
|
||||
def calculate_mime_type(request, path_parts, rev):
|
||||
mime_type = None
|
||||
"""Return a 2-tuple carrying the MIME content type and character
|
||||
encoding for the file represented by PATH_PARTS in REV. Use REQUEST
|
||||
for repository access as necessary."""
|
||||
if not path_parts:
|
||||
return None
|
||||
return None, None
|
||||
mime_type = encoding = None
|
||||
if request.roottype == 'svn' \
|
||||
and (not request.cfg.options.svn_ignore_mimetype):
|
||||
try:
|
||||
itemprops = request.repos.itemprops(path_parts, rev)
|
||||
mime_type = itemprops.get('svn:mime-type')
|
||||
if mime_type:
|
||||
return mime_type
|
||||
mime_type, parameters = parse_mime_type(mime_type)
|
||||
return mime_type, parameters.get('charset')
|
||||
except:
|
||||
pass
|
||||
return guess_mime(path_parts[-1])
|
||||
return guess_mime(path_parts[-1]), None
|
||||
|
||||
def markup_or_annotate(request, is_annotate):
|
||||
cfg = request.cfg
|
||||
@@ -1660,7 +1708,7 @@ def markup_or_annotate(request, is_annotate):
|
||||
lines = fp = image_src_href = None
|
||||
annotation = 'none'
|
||||
revision = None
|
||||
mime_type = calculate_mime_type(request, path, rev)
|
||||
mime_type, encoding = calculate_mime_type(request, path, rev)
|
||||
|
||||
# Is this a viewable image type?
|
||||
if is_viewable_image(mime_type) \
|
||||
@@ -1693,7 +1741,7 @@ def markup_or_annotate(request, is_annotate):
|
||||
fp.close()
|
||||
return
|
||||
lines = markup_stream_pygments(request, cfg, blame_source, fp,
|
||||
path[-1], mime_type)
|
||||
path[-1], mime_type, encoding)
|
||||
fp.close()
|
||||
|
||||
data = common_template_data(request, revision)
|
||||
@@ -1727,7 +1775,7 @@ def markup_or_annotate(request, is_annotate):
|
||||
data['date'] = make_time_string(entry.date, cfg)
|
||||
data['author'] = entry.author
|
||||
data['changed'] = entry.changed
|
||||
data['log'] = format_log(entry.log, cfg)
|
||||
data['log'] = format_log(request, entry.log)
|
||||
data['size'] = entry.size
|
||||
|
||||
if entry.date is not None:
|
||||
@@ -1943,8 +1991,8 @@ 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(file.log, cfg)
|
||||
row.short_log = format_log(file.log, cfg,
|
||||
row.log = format_log(request, file.log)
|
||||
row.short_log = format_log(request, file.log,
|
||||
maxlen=cfg.options.short_log_len)
|
||||
row.lockinfo = file.lockinfo
|
||||
row.anchor = request.server.escape(file.name)
|
||||
@@ -2001,8 +2049,9 @@ def view_directory(request):
|
||||
if request.roottype == 'svn':
|
||||
row.size = file.size
|
||||
|
||||
row.mime_type = calculate_mime_type(request, _path_parts(file_where),
|
||||
file.rev)
|
||||
row.mime_type, encoding = calculate_mime_type(request,
|
||||
_path_parts(file_where),
|
||||
file.rev)
|
||||
fvi = get_file_view_info(request, file_where, file.rev, row.mime_type)
|
||||
row.view_href = fvi.view_href
|
||||
row.download_href = fvi.download_href
|
||||
@@ -2273,9 +2322,11 @@ def view_log(request):
|
||||
if request.roottype == 'cvs':
|
||||
raise debug.ViewVCException('Unsupported feature: log view on CVS '
|
||||
'directory', '400 Bad Request')
|
||||
mime_type = None
|
||||
mime_type = encoding = None
|
||||
else:
|
||||
mime_type = calculate_mime_type(request, request.path_parts, request.pathrev)
|
||||
mime_type, encoding = calculate_mime_type(request,
|
||||
request.path_parts,
|
||||
request.pathrev)
|
||||
|
||||
options = {}
|
||||
options['svn_show_all_dir_logs'] = 1 ### someday make this optional?
|
||||
@@ -2318,7 +2369,7 @@ 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(rev.log or '', cfg)
|
||||
entry.log = format_log(request, rev.log or '')
|
||||
entry.size = rev.size
|
||||
entry.lockinfo = rev.lockinfo
|
||||
entry.branch_point = None
|
||||
@@ -2576,10 +2627,11 @@ def view_checkout(request):
|
||||
|
||||
# The revision number acts as a strong validator.
|
||||
if not check_freshness(request, None, revision):
|
||||
mime_type, encoding = calculate_mime_type(request, path, rev)
|
||||
mime_type = request.query_dict.get('content-type') \
|
||||
or calculate_mime_type(request, path, rev) \
|
||||
or mime_type \
|
||||
or 'text/plain'
|
||||
server_fp = get_writeready_server_file(request, mime_type)
|
||||
server_fp = get_writeready_server_file(request, mime_type, encoding)
|
||||
copy_stream(fp, server_fp)
|
||||
fp.close()
|
||||
|
||||
@@ -3164,7 +3216,7 @@ 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(log_entry1.log, cfg),
|
||||
log=format_log(request, log_entry1.log),
|
||||
size=log_entry1.size,
|
||||
ago=ago1,
|
||||
path=path_left,
|
||||
@@ -3180,7 +3232,7 @@ 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(log_entry2.log, cfg),
|
||||
log=format_log(request, log_entry2.log),
|
||||
size=log_entry2.size,
|
||||
ago=ago2,
|
||||
path=path_right,
|
||||
@@ -3401,7 +3453,7 @@ def download_tarball(request):
|
||||
|
||||
|
||||
def view_revision(request):
|
||||
if request.roottype == "cvs":
|
||||
if request.roottype != "svn":
|
||||
raise debug.ViewVCException("Revision view not supported for CVS "
|
||||
"repositories at this time.",
|
||||
"400 Bad Request")
|
||||
@@ -3420,9 +3472,29 @@ def view_revision(request):
|
||||
return
|
||||
|
||||
# Fetch the revision information.
|
||||
date, author, msg, changes = request.repos.revinfo(rev)
|
||||
date, author, msg, revprops, changes = request.repos.revinfo(rev)
|
||||
date_str = make_time_string(date, cfg)
|
||||
|
||||
# Fix up the revprops list (rather like get_itemprops()).
|
||||
propnames = revprops.keys()
|
||||
propnames.sort()
|
||||
props = []
|
||||
for name in propnames:
|
||||
value = format_log(request, revprops[name])
|
||||
undisplayable = ezt.boolean(0)
|
||||
# skip non-utf8 property names
|
||||
try:
|
||||
unicode(name, 'utf8')
|
||||
except:
|
||||
continue
|
||||
# note non-utf8 property values
|
||||
try:
|
||||
unicode(value, 'utf8')
|
||||
except:
|
||||
value = None
|
||||
undisplayable = ezt.boolean(1)
|
||||
props.append(_item(name=name, value=value, undisplayable=undisplayable))
|
||||
|
||||
# Sort the changes list by path.
|
||||
def changes_sort_by_path(a, b):
|
||||
return cmp(a.path_parts, b.path_parts)
|
||||
@@ -3537,7 +3609,8 @@ def view_revision(request):
|
||||
'rev' : str(rev),
|
||||
'author' : author,
|
||||
'date' : date_str,
|
||||
'log' : format_log(msg, cfg),
|
||||
'log' : format_log(request, msg),
|
||||
'properties' : props,
|
||||
'ago' : date is not None and html_time(request, date, 1) or None,
|
||||
'changes' : changes,
|
||||
'prev_href' : prev_rev_href,
|
||||
@@ -3845,7 +3918,7 @@ def build_commit(request, files, max_files, dir_strip, format):
|
||||
diff_href = request.get_url(view_func=view_diff,
|
||||
where=where, pathtype=vclib.FILE,
|
||||
params=diff_href_params, escape=1)
|
||||
mime_type = calculate_mime_type(request, path_parts, exam_rev)
|
||||
mime_type, encoding = calculate_mime_type(request, path_parts, exam_rev)
|
||||
prefer_markup = ezt.boolean(default_view(mime_type, cfg) == view_markup)
|
||||
|
||||
# Update plus/minus line change count.
|
||||
@@ -3894,8 +3967,8 @@ def build_commit(request, files, max_files, dir_strip, format):
|
||||
commit.log = None
|
||||
commit.short_log = None
|
||||
else:
|
||||
commit.log = format_log(desc, cfg, 0, format != 'rss')
|
||||
commit.short_log = format_log(desc, cfg, cfg.options.short_log_len,
|
||||
commit.log = format_log(request, desc, 0, format != 'rss')
|
||||
commit.short_log = format_log(request, desc, cfg.options.short_log_len,
|
||||
format != 'rss')
|
||||
commit.author = request.server.escape(author)
|
||||
commit.rss_date = make_rss_time_string(date, request.cfg)
|
||||
@@ -4202,8 +4275,8 @@ def expand_root_parents(cfg):
|
||||
pos = string.rfind(pp, ':')
|
||||
if pos < 0:
|
||||
raise debug.ViewVCException(
|
||||
"The path '%s' in 'root_parents' does not include a "
|
||||
"repository type." % (pp))
|
||||
'The path "%s" in "root_parents" does not include a '
|
||||
'repository type. Expected "cvs" or "svn".' % (pp))
|
||||
|
||||
repo_type = string.strip(pp[pos+1:])
|
||||
pp = os.path.normpath(string.strip(pp[:pos]))
|
||||
@@ -4218,8 +4291,9 @@ def expand_root_parents(cfg):
|
||||
cfg.general.svn_roots.update(roots)
|
||||
else:
|
||||
raise debug.ViewVCException(
|
||||
"The path '%s' in 'root_parents' has an unrecognized "
|
||||
"repository type." % (pp))
|
||||
'The path "%s" in "root_parents" has an unrecognized '
|
||||
'repository type ("%s"). Expected "cvs" or "svn".'
|
||||
% (pp, repo_type))
|
||||
|
||||
def find_root_in_parents(cfg, rootname, roottype):
|
||||
"""Return the rootpath for configured ROOTNAME of ROOTTYPE."""
|
||||
|
@@ -38,48 +38,70 @@ numbers, and not literal):
|
||||
should exactly reflect what you wish to distribute and dub "the
|
||||
release".
|
||||
|
||||
7. Edit the file 'lib/viewvc.py' and remove the "-dev" suffix from
|
||||
7. Update your release branch working copy to HEAD.
|
||||
|
||||
svn up
|
||||
|
||||
8. Edit the file 'lib/viewvc.py' and remove the "-dev" suffix from
|
||||
__version__. The remainder should be of the form "X.Y.Z", where X,
|
||||
Y, and Z are positive integers. Do NOT commit this change.
|
||||
Y, and Z are positive integers.
|
||||
|
||||
8. Update your working copy to HEAD, and tag the release:
|
||||
*** Do NOT commit this change. ***
|
||||
|
||||
svn up && svn cp -m "Tag the X.Y.Z final release." . ^/tags/X.Y.Z
|
||||
9. "Peg" the contributed templates externals definition to the
|
||||
current HEAD revision:
|
||||
|
||||
9. Go into an empty directory and run the 'make-release' script:
|
||||
svn pedit svn:externals .
|
||||
|
||||
(squeeze "-rBASE_REV", where BASE_REV is the current HEAD revision
|
||||
number, between 'templates-contrib' and the target URL).
|
||||
|
||||
*** Do NOT commit this change. ***
|
||||
|
||||
10. Tag the release:
|
||||
|
||||
svn cp -m "Tag the X.Y.Z final release." . ^/tags/X.Y.Z
|
||||
|
||||
This will create a copy of the release branch, plus your local
|
||||
modifications to the svn:externals property and lib/viewvc.py
|
||||
file, to the tag location.
|
||||
|
||||
11. Revert the changes in your working copy.
|
||||
|
||||
svn revert -R .
|
||||
|
||||
12. Go into an empty directory and run the 'make-release' script:
|
||||
|
||||
tools/make-release viewvc-X.Y.Z tags/X.Y.Z
|
||||
|
||||
10. Verify the archive files:
|
||||
13. Verify the archive files:
|
||||
|
||||
- do they have a LICENSE.html file?
|
||||
- do they have necessary include documentation?
|
||||
- do they *not* have unnecessary stuff?
|
||||
- do they install and work correctly?
|
||||
|
||||
11. Upload the created archive files (tar.gz and zip) into the Files
|
||||
14. Upload the created archive files (tar.gz and zip) into the Files
|
||||
and Documents section of the Tigris.org project, and modify the
|
||||
CHECKSUMS document there accordingly. Also, drop a copy of the
|
||||
archive files into the root directory of the viewvc.org website
|
||||
(unversioned).
|
||||
|
||||
12. On trunk, update the websites (both the viewvc.org/ and www/ ones)
|
||||
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.
|
||||
|
||||
13. Edit the file 'lib/viewvc.py' again, re-adding the "-dev" suffix
|
||||
and incrementing the patch number assigned to the __version__
|
||||
variable, and add a new empty block in the branch's CHANGES file,
|
||||
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 "Begin a new release cycle."
|
||||
|
||||
14. Edit the Issue Tracker configuration options, adding a new Version
|
||||
17. Edit the Issue Tracker configuration options, adding a new Version
|
||||
for the just-released one, and a new Milestone for the next patch
|
||||
(and possibly, minor or major) release. (For the Milestone sort
|
||||
key, use a packed integer XXYYZZ: 1.0.3 == 10003, 2.11.4 == 21104.)
|
||||
|
||||
15. Write an announcement explaining all the cool new features and
|
||||
post it to the announce@ list, to the project's News area, and to
|
||||
other places interested in this sort of stuff, such as Freshmeat
|
||||
(http://www.freshmeat.net).
|
||||
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).
|
||||
|
@@ -94,9 +94,6 @@ form { margin: 0; }
|
||||
.vc_properties {
|
||||
margin: 1em 0;
|
||||
}
|
||||
.vc_properties h2 {
|
||||
font-size: 115%;
|
||||
}
|
||||
|
||||
|
||||
/*** File Content Markup Styles ***/
|
||||
|
@@ -5,7 +5,7 @@
|
||||
<table>
|
||||
<tr>
|
||||
<td>[if-any cfg.general.address]<address><a href="mailto:[cfg.general.address]">[cfg.general.address]</a></address>[else] [end]</td>
|
||||
<td style="text-align: right;"><strong><a href="[help_href]">ViewVC Help</a></strong></td>
|
||||
<td style="text-align: right;">[if-any help_href]<strong><a href="[help_href]">ViewVC Help</a></strong>[else] [end]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Powered by <a href="http://viewvc.tigris.org/">ViewVC [vsn]</a></td>
|
||||
|
@@ -4,13 +4,13 @@
|
||||
<!-- ViewVC :: http://www.viewvc.org/ -->
|
||||
<head>
|
||||
<title>Checkin Database Query</title>
|
||||
<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />
|
||||
[if-any docroot]<link rel="stylesheet" href="[docroot]/styles.css" type="text/css" />[end]
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
[# setup page definitions]
|
||||
[define help_href][docroot]/help_query.html[end]
|
||||
[define help_href][if-any docroot][docroot]/help_query.html[end][end]
|
||||
[# end]
|
||||
|
||||
<p>
|
||||
@@ -202,21 +202,21 @@
|
||||
</td>
|
||||
|
||||
[# uncommment, if you want a separate Description column:
|
||||
{if-index commits.files first{
|
||||
{if-index commits.files first}
|
||||
<td style="vertical-align:top;" rowspan="{commits.num_files}">
|
||||
{commits.log}
|
||||
{if-any commits.log}{commits.log}{else} {end}
|
||||
</td>
|
||||
{end}
|
||||
|
||||
(substitute brackets for the braces)
|
||||
]
|
||||
</tr>
|
||||
[# and also take the following out in the "Description column"-case:]
|
||||
[# and also take the following out in the "Description column" case:]
|
||||
[if-index commits.files last]
|
||||
<tr class="vc_row_[if-index commits even]even[else]odd[end]">
|
||||
<td> </td>
|
||||
<td colspan="5"><strong>Log:</strong><br />
|
||||
<pre class="vc_log">[commits.log]</pre></td>
|
||||
<pre class="vc_log">[if-any commits.log][commits.log][else] [end]</pre></td>
|
||||
</tr>
|
||||
[end]
|
||||
[# ---]
|
||||
|
@@ -6,6 +6,7 @@
|
||||
[include "include/header.ezt" "revision"]
|
||||
|
||||
<hr />
|
||||
|
||||
<form method="get" action="[jump_rev_action]">
|
||||
<table cellspacing="1" cellpadding="2" style="width: auto;">
|
||||
<tr align="left">
|
||||
@@ -42,7 +43,7 @@
|
||||
|
||||
<hr />
|
||||
|
||||
<p><strong>Changed paths:</strong></p>
|
||||
<h2>Changed paths</h2>
|
||||
|
||||
<table cellspacing="1" cellpadding="2">
|
||||
<thead>
|
||||
@@ -77,4 +78,5 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
[include "include/props.ezt"]
|
||||
[include "include/footer.ezt"]
|
||||
|
@@ -50,7 +50,9 @@ 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/wsgi/viewvc.wsgi", "bin/wsgi/viewvc.wsgi", 0755, 1, 0, 0),
|
||||
("bin/wsgi/viewvc.fcgi", "bin/wsgi/viewvc.fcgi", 0755, 1, 0, 0),
|
||||
("bin/wsgi/query.wsgi", "bin/wsgi/query.wsgi", 0755, 1, 0, 0),
|
||||
("bin/wsgi/query.fcgi", "bin/wsgi/query.fcgi", 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),
|
||||
|
Reference in New Issue
Block a user