Big update! Convert the rest of the pages to use templates.

* annotation, human readable diff, CvsGraph, and a marked-up file all
  use a template to generate the basic parts of the page. In many
  cases (annotate, graph, markup), the content is still rendered
  by other code.

* for the diff support, we take advantage of the new list iteration
  support in EZT to "iterate on" a DiffSource object, which parses a
  diff stream into structured object for the template to display.

* for the CvsGraph page, we make use of EZT's new stream support to
  stream in an imagemap from the CvsGraph executable. the graph page
  includes a navigation header now.

* updated the upgrading.html page with some items missing from the 0.7
  instructions, plus new instructions.

* because of the complete conversion, the configuration file no longer
  specifies colors or images -- these are all in the templates now.
  Removed the relevant material from config.py and viewcvs.conf.dist.

* converted html_footer() to use the new footer.ezt template because
  we still have to call that manually in the markup and annotate
  cases.

* add the templates to viewcvs.conf.dist and config.py

* added a handy generate_page() utility for generating pages from a
  template and its input data.

* removed download_link, html_icon, html_option, print_diff_select,
  and navigate_header utility functions. These all printed HTML to the
  output, but are obsolete due to the new templates.

* the new nav_header_data() utility function fills in data used by the
  "header.ezt" template, which replaces the navigate_header() func.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@395 8cb11bc2-c004-0410-86c3-e597b4017df7
remotes/tags/V0_9
gstein 2001-12-18 00:22:14 +00:00
parent 830c6ba76c
commit d71c208540
9 changed files with 554 additions and 388 deletions

View File

@ -145,11 +145,17 @@ forbidden =
#
directory = templates/directory.ezt
query = templates/query.ezt
footer = templates/footer.ezt
diff = templates/diff.ezt
graph = templates/graph.ezt
annotate = templates/annotate.ezt
markup = templates/annotate.ezt
log = templates/log.ezt
# There is also a new style table based alternative template available.
# You might want to try it out:
# log = templates/log_table.ezt
query = templates/query.ezt
#---------------------------------------------------------------------------
[cvsdb]
@ -162,59 +168,6 @@ query = templates/query.ezt
#readonly_passwd =
#row_limit = 1000
#---------------------------------------------------------------------------
[images]
#
# All images are defined with three values: URL, WIDTH, HEIGHT
#
#
# these icons represent a back-pointer, a directory (folder), and a file.
# they are normally available in a standard Apache distribution, along
# with larger versions if these are too small for you.
#
back_icon = /icons/small/back.gif, 16, 16
dir_icon = /icons/small/dir.gif, 16, 16
file_icon = /icons/small/text.gif, 16, 16
#---------------------------------------------------------------------------
[colors]
# background color of log entry in markup
markup_log = #ffffff
# The following six colors are used together:
# color of change-section headings in a diff (default turquoise)
diff_heading = #99cccc
# color of "empty" lines (default light gray)
diff_empty = #cccccc
# removed lines (default light red)
diff_remove = #ffaaaa
# changed lines (default light yellow)
diff_change = #ffff77
# added lines (default light green)
diff_add = #aaffaa
# empty lines in a change block (if one part smaller is than the other;
# default is a greyish yellow: the color should match the hue of 'diff_change')
diff_dark_change = #eeee77
# navigation header (in diff screen, file view, annotation, etc)
nav_header = #9999ee
# color of text on most pages
text = #000000
# color of standard background
background = #ffffff
# color of alternate background (diffs, file view, annotations, etc)
alt_background = #eeeeee
#---------------------------------------------------------------------------
[options]
### DOC

View File

@ -41,7 +41,7 @@ import fnmatch
#########################################################################
class Config:
_sections = ('general', 'images', 'options', 'colors', 'cvsdb', 'templates')
_sections = ('general', 'options', 'cvsdb', 'templates')
_force_multi_value = ('cvs_roots', 'forbidden', 'disable_enscript_lang')
def __init__(self):
@ -66,7 +66,7 @@ class Config:
for opt in parser.options(section):
value = parser.get(section, opt)
if opt in self._force_multi_value or subcfg_name == 'images':
if opt in self._force_multi_value:
value = map(string.strip, filter(None, string.split(value, ',')))
else:
try:
@ -118,12 +118,16 @@ class Config:
self.general.rcs_path = ''
self.general.mime_types_file = ''
self.general.address = '<a href="mailto:user@insert.your.domain.here">No CVS admin address has been configured</a>'
self.general.main_title = 'CVS Repository'
self.general.forbidden = ()
self.templates.directory = 'templates/directory.ezt'
self.templates.log = 'templates/log.ezt'
self.templates.query = 'templates/query.ezt'
self.templates.footer = 'templates/footer.ezt'
self.templates.diff = 'templates/diff.ezt'
self.templates.graph = 'templates/graph.ezt'
self.templates.annotate = 'templates/annotate.ezt'
self.templates.markup = 'templates/markup.ezt'
self.cvsdb.enabled = 0
self.cvsdb.host = ''
@ -134,26 +138,6 @@ class Config:
self.cvsdb.readonly_passwd = ''
self.cvsdb.row_limit = 1000
self.images.back_icon = "/icons/small/back.gif", 16, 16
self.images.dir_icon = "/icons/small/dir.gif", 16, 16
self.images.file_icon = "/icons/small/text.gif", 16, 16
self.colors.markup_log = "#ffffff"
self.colors.diff_heading = "#99cccc"
self.colors.diff_empty = "#cccccc"
# trafic light methaphor:
self.colors.diff_remove = "#ffaaaa" # red
self.colors.diff_change = "#ffff77" # yellow/orange
self.colors.diff_add = "#aaffaa" # green
self.colors.diff_dark_change = "#eeee77" # meets hue of diff_change
self.colors.nav_header = "#9999ee"
self.colors.text = "#000000"
self.colors.background = "#ffffff"
self.colors.alt_background = "#eeeeee"
self.options.sort_by = 'file'
self.options.hide_attic = 1
self.options.log_sort = 'date'
@ -168,8 +152,6 @@ class Config:
self.options.allow_annotate = 1
self.options.allow_markup = 1
self.options.allow_compress = 1
self.options.use_java_script = 1
self.options.open_extern_window = 1
self.options.checkout_magic = 1
self.options.show_subdir_lastmod = 0
self.options.flip_links_in_dirview = 0
@ -202,13 +184,7 @@ class Config:
return default
class _sub_config:
def get_image(self, which):
text = '[%s]' % string.upper(which)
path, width, height = getattr(self, which)
if path:
return '<img src="%s" alt="%s" border=0 width=%s height=%s>' % \
(path, text, width, height)
return text
pass
if not hasattr(sys, 'hexversion'):
# Python 1.5 or 1.5.1. fix the syntax for ConfigParser options.

View File

@ -206,7 +206,6 @@ class Request:
self.mime_type, self.encoding = mimetypes.guess_type(self.where)
if not self.mime_type:
self.mime_type = 'text/plain'
self.default_text_plain = self.mime_type == 'text/plain'
self.default_viewable = cfg.options.allow_markup and \
is_viewable(self.mime_type)
@ -244,6 +243,13 @@ def error(msg, status='500 Internal Server Error'):
print msg
sys.exit(0)
def generate_page(tname, data):
debug.t_start()
template = ezt.Template(os.path.join(g_install_dir, tname))
debug.t_end('ezt-parse')
template.generate(sys.stdout, data)
_header_sent = 0
def http_header(content_type='text/html'):
global _header_sent
@ -254,11 +260,15 @@ def http_header(content_type='text/html'):
_header_sent = 1
def html_footer():
print '<hr noshade><table width="100&#37;" border=0 cellpadding=0 cellspacing=0><tr>'
print '<td align=left><address>%s</address></td>' % cfg.general.address
print '<td align=right>Powered by<br><a href="http://viewcvs.sourceforge.net/">ViewCVS %s</a>' % __version__
print '</td></tr></table>'
print '</body></html>'
### would be nice to have a "standard" set of data available to all
### templates. should move that to the request ob, probably
data = {
'cfg' : cfg,
'vsn' : __version__,
}
# generate the footer
generate_page(cfg.templates.footer, data)
def sticky_query(dict):
sticky_dict = { }
@ -342,31 +352,6 @@ def download_url(request, url, revision, mime_type):
return url + '&content-type=' + mime_type
return url
def download_link(request, url, revision, text, mime_type=None):
full_url = download_url(request, url, revision, mime_type)
paren = text[0] == '('
lparen = rparen = ''
if paren:
lparen = '('
rparen = ')'
text = text[1:-1]
print '%s<a href="%s"' % (lparen, full_url)
if cfg.options.open_extern_window and mime_type != viewcvs_mime_type:
print ' target="cvs_checkout"'
if cfg.options.use_java_script:
print " onClick=\"window.open('about:blank','cvs_checkout'," \
"'resizeable=1,scrollbars=1",
if mime_type == 'text/html':
print ',status,toolbar',
print "');\""
print '><b>%s</b></a>%s' % (text, rparen)
def html_icon(which):
return cfg.images.get_image(which + '_icon')
def plural(num, text):
if num == 1:
return '1 ' + text
@ -405,51 +390,16 @@ def html_time(secs, extended=0):
s = s + ', ' + ext
return s
def html_option(value, cur_value, text=None):
if text is None:
attr = ''
text = value
else:
attr = 'value="%s"' % value
if value == cur_value:
print '<option %s selected>%s</option>' % (attr, text)
else:
print '<option %s>%s</option>' % (attr, text)
def print_diff_select(query_dict):
print '<select name="diff_format"'
if cfg.options.use_java_script:
print 'onchange="submit()"'
print '>'
format = query_dict['diff_format']
html_option('h', format, 'Colored Diff')
html_option('l', format, 'Long Colored Diff')
html_option('u', format, 'Unidiff')
html_option('c', format, 'Context Diff')
html_option('s', format, 'Side by Side')
print '</select>'
def navigate_header(request, swhere, path, filename, rev, title):
if swhere == request.url:
swhere = urllib.quote(filename)
print '<html><head>'
print header_comment
print '<title>%s/%s - %s - %s</title></head>' % (path, filename, title, rev)
print '<body bgcolor="%s">' % cfg.colors.alt_background
print '<table width="100&#37;" border=0 cellspacing=0 cellpadding=1 bgcolor="%s">' % cfg.colors.nav_header
print '<tr valign=bottom><td>'
print '<a href="%s%s#rev%s">%s</a>' % \
(swhere, request.qmark_query, rev, html_icon('back'))
print '<b>Return to %s CVS log</b> %s</td>' % \
(html_link(filename,
'%s%s#rev%s' % (swhere, request.qmark_query, rev)),
html_icon('file'))
print '<td align=right>%s <b>Up to %s</b></td>' % \
(html_icon('dir'), clickable_path(request, path, 1, 0, 0))
print '</tr></table>'
def nav_header_data(request, path, filename, rev, title):
return {
'title' : title,
'nav_path' : clickable_path(request, path, 1, 0, 0),
'path' : path,
'filename' : filename,
'file_url' : urllib.quote(filename),
'rev' : rev,
'qquery' : request.qmark_query,
}
def copy_stream(fp):
while 1:
@ -600,29 +550,65 @@ def markup_stream(request, fp, revision, mime_type):
pathname = pathname[:-6]
file_url = urllib.quote(filename)
http_header()
navigate_header(request, request.url, pathname, filename, revision, 'view')
print '<hr noshade>'
print '<table width="100&#37;"><tr><td bgcolor="%s">' % cfg.colors.markup_log
print 'File:', clickable_path(request, where, 1, 1, 0), '</b>'
download_link(request, file_url, revision, '(download)')
if not request.default_text_plain:
download_link(request, file_url, revision, '(as text)', 'text/plain')
print '<br>'
data = nav_header_data(request, pathname, filename, revision, 'view')
data.update({
'request' : request,
'cfg' : cfg,
'vsn' : __version__,
'nav_path' : clickable_path(request, where, 1, 1, 0),
'href' : download_url(request, file_url, revision, None),
'text_href' : download_url(request, file_url, revision, 'text/plain'),
'mime_type' : request.mime_type,
'log' : None,
})
if cfg.options.show_log_in_markup:
show_revs, rev_map, rev_order, taginfo, rev2tag, \
cur_branch, branch_points, branch_names = read_log(full_name)
entry = rev_map[revision]
print_log(request, rev_map, rev_order, rev_map[revision], rev2tag,
branch_points)
idx = string.rfind(revision, '.')
branch = revision[:idx]
if _re_is_vendor_branch.match(revision):
data['vendor_branch'] = 'yes'
else:
data['vendor_branch'] = None
data.update({
'utc_date' : time.asctime(time.gmtime(entry.date)),
'ago' : html_time(entry.date, 1),
'author' : entry.author,
'branches' : None,
'tags' : None,
'branch_points' : None,
'changed' : entry.changed,
'log' : htmlify(entry.log),
'state' : entry.state,
})
if rev2tag.has_key(branch):
data['branches'] = string.join(rev2tag[branch], ', ')
if rev2tag.has_key(revision):
data['tags'] = string.join(rev2tag[revision], ', ')
if branch_points.has_key(revision):
data['branch_points'] = string.join(branch_points[revision], ', ')
prev_rev = string.split(revision, '.')
while 1:
if prev_rev[-1] == '0': # .0 can be caused by 'commit -r X.Y.Z.0'
prev_rev = prev_rev[:-2] # X.Y.Z.0 becomes X.Y.Z
else:
prev_rev[-1] = str(int(prev_rev[-1]) - 1)
prev = string.join(prev_rev, '.')
if rev_map.has_key(prev) or prev == '':
break
data['prev'] = prev
else:
print 'Version: <b>%s</b><br>' % revision
tag = query_dict.get('only_with_tag')
if tag:
print 'Tag: <b>%s</b><br>' % tag
print '</td></tr></table>'
data['tag'] = query_dict.get('only_with_tag')
http_header()
generate_page(cfg.templates.markup, data)
print '<hr noshade>'
if mime_type[:6] == 'image/':
url = download_url(request, file_url, revision, mime_type)
print '<img src="%s"><br>' % url
@ -1126,13 +1112,6 @@ def view_directory(request):
for file in attic_files:
file_data.append((file, None, 0))
http_header()
debug.t_start()
template = ezt.Template()
template.parse_file(os.path.join(g_install_dir, cfg.templates.directory))
debug.t_end('ezt-parse')
# prepare the data that will be passed to the template
data = {
'where' : where,
@ -1438,8 +1417,8 @@ def view_directory(request):
url = url + '&' + query
data['tarball_href'] = url
# generate the page
template.generate(sys.stdout, data)
http_header()
generate_page(cfg.templates.directory, data)
def fetch_log(full_name, which_rev=None):
if which_rev:
@ -1613,6 +1592,8 @@ def read_log(full_name, which_rev=None, view_tag=None, logsort='cvs'):
return show_revs, rev_map, rev_order, taginfo, rev2tag, \
cur_branch, branch_points, branch_names
_re_is_vendor_branch = re.compile(r'^1\.1\.1\.\d+$')
g_name_printed = { } ### gawd, what a hack...
def augment_entry(entry, request, file_url, rev_map, rev2tag, branch_points,
rev_order, extended):
@ -1716,50 +1697,6 @@ def augment_entry(entry, request, file_url, rev_map, rev2tag, branch_points,
else:
entry.to_selected = None
_re_is_vendor_branch = re.compile(r'^1\.1\.1\.\d+$')
def print_log(request, rev_map, rev_order, entry, rev2tag, branch_points):
query_dict = request.query_dict
where = request.where
rev = entry.rev
idx = string.rfind(rev, '.')
branch = rev[:idx]
print 'Revision <b>%s</b>' % rev
if _re_is_vendor_branch.match(rev):
print '<i>(vendor branch)</i>'
print ', <i>%s UTC</i> (%s ago) by <i>%s</i>' % \
(time.asctime(time.gmtime(entry.date)),
html_time(entry.date, 1),
entry.author)
if rev2tag.has_key(branch):
print '<br>Branch: <b>%s</b>' % string.join(rev2tag[branch], ', ')
if rev2tag.has_key(rev):
print '<br>CVS Tags: <b>%s</b>' % string.join(rev2tag[rev], ', ')
if branch_points.has_key(rev):
print '<br>Branch point for: <b>%s</b>' % \
string.join(branch_points[rev], ', ')
prev_rev = string.split(rev, '.')
while 1:
if prev_rev[-1] == '0': # .0 can be caused by 'commit -r X.Y.Z.0'
prev_rev = prev_rev[:-2] # X.Y.Z.0 becomes X.Y.Z
else:
prev_rev[-1] = str(int(prev_rev[-1]) - 1)
prev = string.join(prev_rev, '.')
if rev_map.has_key(prev) or prev == '':
break
if prev and entry.changed:
print '<br>Changes since <b>%s: %s lines</b>' % (prev, entry.changed)
if entry.state == 'dead':
print '<br><b><i>FILE REMOVED</i></b>'
print '<pre>' + htmlify(entry.log) + '</pre>'
def view_log(request):
full_name = request.full_name
where = request.where
@ -1888,15 +1825,8 @@ def view_log(request):
branch_names.reverse()
data['branch_names'] = branch_names
debug.t_start()
template = ezt.Template()
template.parse_file(os.path.join(g_install_dir, cfg.templates.log))
debug.t_end('ezt-parse')
http_header()
# generate the page
template.generate(sys.stdout, data)
generate_page(cfg.templates.log, data)
### suck up other warnings in _re_co_warning?
_re_co_filename = re.compile(r'^(.*),v\s+-->\s+standard output\s*\n$')
@ -1996,10 +1926,16 @@ def view_annotate(request):
if pathname[-6:] == '/Attic':
pathname = pathname[:-6]
http_header()
navigate_header(request, request.url, pathname, filename, rev, 'view')
print '<hr noshade>'
data = nav_header_data(request, pathname, filename, rev, 'annotate')
data.update({
'cfg' : cfg,
'vsn' : __version__,
})
http_header()
generate_page(cfg.templates.annotate, data)
### be nice to hook this into the template...
import blame
blame.make_html(request.cvsroot, request.where + ',v', rev,
sticky_query(request.query_dict))
@ -2028,42 +1964,30 @@ def view_cvsgraph(cfg, request):
if pathname[-6:] == '/Attic':
pathname = pathname[:-6]
http_header()
# FIXME: use navigate_header(request, request.url, pathname, filename, rev, 'view')
# FIXME: Move this into a template ?
print """<html>
<head>
<title>Revision graph of %s</title>
</head>
<body bgcolor="#f0f0f0">
<center>
<h1>Revision graph of %s</h1>""" % (where, where)
#" fix Emacs font-lock: close a quote above
data = nav_header_data(request, pathname, filename, rev, 'graph')
# Required only if cvsgraph needs to find it's supporting libraries.
# Uncomment and set accordingly if required.
#os.environ['LD_LIBRARY_PATH'] = '/usr/lib:/usr/local/lib'
# Create an image map
fp = popen.popen(os.path.normpath(os.path.join(cfg.options.cvsgraph_path,'cvsgraph')),
fp = popen.popen(os.path.join(cfg.options.cvsgraph_path, 'cvsgraph'),
("-i",
"-c", cfg.options.cvsgraph_conf,
"-r", request.cvsroot,
"-6", request.amp_query,
"-7", request.qmark_query,
request.where + ',v'), 'r')
copy_stream(fp)
fp.close()
print """<img border="0"
usemap="#MyMapName"
src="%s?graph=%s&makeimage=1%s"
alt="Revisions of %s">""" % (request.url,
rev, request.amp_query, where)
#" fix Emacs font-lock: close a quote above
print '</center>'
html_footer()
data.update({
'request' : request,
'imagemap' : fp,
'cfg' : cfg,
'vsn' : __version__,
})
http_header()
generate_page(cfg.templates.graph, data)
def search_files(request, search_re):
# Pass in Request object and the search regular expression. We check out
@ -2153,18 +2077,19 @@ def view_doc(request):
copy_stream(fp)
fp.close()
_re_extract_rev = re.compile(r'^[-+]+ [^\t]+\t([^\t]+)\t((\d+\.)+\d+)$')
_re_extract_info = re.compile(r'@@ \-([0-9]+).*\+([0-9]+).*@@(.*)')
_re_extract_diff = re.compile(r'^([-+ ])(.*)')
def human_readable_diff(request, fp, rev1, rev2, sym1, sym2):
# do this now, in case we need to print an error
http_header()
query_dict = request.query_dict
where_nd = request.where[:-5] # remove the ".diff"
pathname, filename = os.path.split(where_nd)
navigate_header(request, request.script_name + '/' + where_nd, pathname,
filename, rev2, 'diff')
data = nav_header_data(request, pathname, filename, rev2, 'diff')
log_rev1 = log_rev2 = None
date1 = date2 = ''
@ -2191,6 +2116,7 @@ def human_readable_diff(request, fp, rev1, rev2, sym1, sym2):
break
if (log_rev1 and log_rev1 != rev1) or (log_rev2 and log_rev2 != rev2):
### it would be nice to have an error.ezt for things like this
print '<strong>ERROR:</strong> rcsdiff did not return the correct'
print 'version number in its output.'
print '(got "%s" / "%s", expected "%s" / "%s")' % \
@ -2198,127 +2124,31 @@ def human_readable_diff(request, fp, rev1, rev2, sym1, sym2):
print '<p>Aborting operation.'
sys.exit(0)
print '<h3 align=center>Diff for /%s between version %s and %s</h3>' % \
(where_nd, rev1, rev2)
print '<table border=0 cellspacing=0 cellpadding=0 width="100&#37;">'
print '<tr bgcolor=white>'
print '<th width="50&#37;" valign=top>'
print 'version %s%s' % (rev1, date1)
if sym1:
print '<br>Tag:', sym1
print '</th>'
print '<th width="50&#37;" valign=top>'
print 'version %s%s' % (rev2, date2)
if sym2:
print '<br>Tag:', sym2
print '</th></tr>'
fs = '<font face="%s" size="%s">' % \
(cfg.options.diff_font_face, cfg.options.diff_font_size)
left_row = right_row = 0
# this will be set to true if any changes are found
changes_seen = 0
while 1:
line = fp.readline()
if not line:
break
# we've seen some kind of change
changes_seen = 1
if line[:2] == '@@':
match = _re_extract_info.match(line)
print '<tr bgcolor="%s"><td width="50&#37;">' % cfg.colors.diff_heading
print '<table width="100&#37;" border=1 cellpadding=5><tr>'
print '<td><b>Line %s</b>&nbsp;<font size="-1">%s</font></td>' % \
(match.group(1), match.group(3))
print '</tr></table></td><td width="50&#37;">'
print '<table width="100&#37;" border=1 cellpadding=5><tr>'
print '<td><b>Line %s</b>&nbsp;<font size="-1">%s</font></td>' % \
(match.group(2), match.group(3))
print '</tr></table></td></tr>'
state = 'dump'
left_col = [ ]
right_col = [ ]
elif line[0] == '\\':
# \ No newline at end of file
flush_diff_rows(state, left_col, right_col)
left_col = [ ]
right_col = [ ]
else:
match = _re_extract_diff.match(line)
line = spaced_html_text(match.group(2))
# add font stuff
line = fs + '&nbsp;' + line + '</font>'
diff_code = match.group(1)
if diff_code == '+':
if state == 'dump':
print '<tr><td bgcolor="%s">&nbsp;</td>' \
'<td bgcolor="%s">%s</td></tr>' % \
(cfg.colors.diff_empty, cfg.colors.diff_add, line)
else:
state = 'pre-change-add'
right_col.append(line)
elif diff_code == '-':
state = 'pre-change-remove'
left_col.append(line)
else:
flush_diff_rows(state, left_col, right_col)
print '<tr><td>%s</td><td>%s</td></tr>' % (line, line)
state = 'dump'
left_col = [ ]
right_col = [ ]
if changes_seen:
flush_diff_rows(state, left_col, right_col)
else:
print '<tr><td colspan=2>&nbsp;</td></tr>'
print '<tr bgcolor="%s"><td colspan=2 align=center><br><b>- No changes -</b><br>&nbsp;</td></tr>' % (cfg.colors.diff_empty)
print '</table><br><hr noshade width="100&#37;">'
print '<table border=0 cellpadding=10><tr><td>'
# print the legend
print '<table border=1><tr><td>Legend:<br>'
print '<table border=0 cellspacing=0 cellpadding=1>'
print '<tr><td align=center bgcolor="%s">Removed from v.%s</td><td bgcolor="%s">&nbsp;</td></tr>' % (cfg.colors.diff_remove, rev1, cfg.colors.diff_empty)
print '<tr bgcolor="%s"><td align=center colspan=2>changed lines</td></tr>' % cfg.colors.diff_change
print '<tr><td bgcolor="%s">&nbsp;</td><td align=center bgcolor="%s">Added in v.%s</td></tr>' % (cfg.colors.diff_empty, cfg.colors.diff_add, rev2)
print '</table></td></tr></table></td>'
# format selector
print '<td><form method="GET" action="%s">' % request.url
hidden_values = ''
for varname, value in query_dict.items():
if varname != 'diff_format' and value != default_settings.get(varname):
print '<input type=hidden name="%s" value="%s">' % \
(varname, cgi.escape(value))
print_diff_select(query_dict)
print '<input type=submit value="Show"></form></td></tr>'
print '</table>'
html_footer()
hidden_values = hidden_values + \
'<input type=hidden name="%s" value="%s">' % \
(varname, cgi.escape(value))
data.update({
'cfg' : cfg,
'vsn' : __version__,
'request' : request,
'where' : where_nd,
'rev1' : rev1,
'rev2' : rev2,
'tag1' : sym1,
'tag2' : sym2,
'date1' : date1,
'date2' : date2,
'changes' : DiffSource(fp),
'diff_format' : query_dict['diff_format'],
'hidden_values' : hidden_values,
})
def flush_diff_rows(state, left_col, right_col):
if state == 'pre-change-remove':
for row in left_col:
print '<tr><td bgcolor="%s">%s</td><td bgcolor="%s">&nbsp;</td></tr>' % \
(cfg.colors.diff_remove, row, cfg.colors.diff_empty)
elif state == 'pre-change-add':
for i in range(max(len(left_col), len(right_col))):
if i < len(left_col):
left = '<td bgcolor="%s">%s</td>' % (cfg.colors.diff_change, left_col[i])
else:
left = '<td bgcolor="%s">&nbsp;</td>' % cfg.colors.diff_dark_change
if i < len(right_col):
right = '<td bgcolor="%s">%s</td>' % (cfg.colors.diff_change, right_col[i])
else:
right = '<td bgcolor="%s">&nbsp;</td>' % cfg.colors.diff_dark_change
print '<tr>%s%s</tr>' % (left, right)
generate_page(cfg.templates.diff, data)
def spaced_html_text(text):
text = string.expandtabs(string.rstrip(text))
@ -2341,6 +2171,133 @@ def spaced_html_text(text):
text = string.replace(text, '\x02', '<font color=red>\</font><br>')
return text
class DiffSource:
def __init__(self, fp):
self.fp = fp
self.save_line = None
# keep track of where we are during an iteration
self.idx = -1
self.last = None
# these will be set once we start reading
self.left = None
self.right = None
self.state = 'no-changes'
def __getitem__(self, idx):
if idx == self.idx:
return self.last
if idx != self.idx + 1:
raise DiffSequencingError()
# keep calling _get_row until it gives us something. sometimes, it
# doesn't return a row immediately because it is accumulating changes
# when it is out of data, _get_row will raise IndexError
while 1:
item = self._get_row()
if item:
self.idx = idx
self.last = item
open('/tmp/log','a').write('idx=%d item=%s\n' % (idx,vars(item)))
return item
def _get_row(self):
if self.state[:5] == 'flush':
item = self._flush_row()
if item:
return item
self.state = 'dump'
if self.save_line:
line = self.save_line
self.save_line = None
else:
line = self.fp.readline()
if not line:
if self.state == 'no-changes':
self.state == 'done'
return _item(type='no-changes')
# see if there are lines to flush
if self.left_col or self.right_col:
# move into the flushing state
self.state = 'flush-' + self.state
return None
# nothing more to return
raise IndexError
if line[:2] == '@@':
self.state = 'dump'
self.left_col = [ ]
self.right_col = [ ]
match = _re_extract_info.match(line)
return _item(type='header', line1=match.group(1), line2=match.group(2),
extra=match.group(3))
if line[0] == '\\':
# \ No newline at end of file
# move into the flushing state. note: it doesn't matter if we really
# have data to flush or not; that will be figured out later
self.state = 'flush-' + self.state
return None
diff_code = line[0]
output = spaced_html_text(line[1:])
fs = '<font face="%s" size="%s">' % \
(cfg.options.diff_font_face, cfg.options.diff_font_size)
# add font stuff
output = fs + '&nbsp;' + output + '</font>'
if diff_code == '+':
if self.state == 'dump':
return _item(type='add', right=output)
self.state = 'pre-change-add'
self.right_col.append(output)
return None
if diff_code == '-':
self.state = 'pre-change-remove'
self.left_col.append(output)
return None
if self.left_col or self.right_col:
# save the line for processing again later
self.save_line = line
# move into the flushing state
self.state = 'flush-' + self.state
return None
return _item(type='context', left=output, right=output)
def _flush_row(self):
if not self.left_col and not self.right_col:
# nothing more to flush
return None
if self.state == 'flush-pre-change-remove':
return _item(type='remove', left=self.left_col.pop(0))
# state == flush-pre-change-add
item = _item(type='change', have_left=None, have_right=None)
if self.left_col:
item.have_left = 'yes'
item.left = self.left_col.pop(0)
if self.right_col:
item.have_right = 'yes'
item.right = self.right_col.pop(0)
return item
class DiffSequencingError(Exception):
pass
def view_diff(request, cvs_filename):
query_dict = request.query_dict
cvsroot = request.cvsroot
@ -2417,7 +2374,6 @@ def view_diff(request, cvs_filename):
fp = popen.popen(os.path.normpath(os.path.join(cfg.general.rcs_path,'rcsdiff')), args, 'r')
if human_readable:
http_header()
human_readable_diff(request, fp, rev1, rev2, sym1, sym2)
return

3
templates/annotate.ezt Normal file
View File

@ -0,0 +1,3 @@
[include "header.ezt"]
<hr noshade>

88
templates/diff.ezt Normal file
View File

@ -0,0 +1,88 @@
[include "header.ezt"]
<h3 align=center>Diff for /[where] between version [rev1] and [rev2]</h3>
<table border=0 cellspacing=0 cellpadding=0 width="100%">
<tr bgcolor=white>
<th width="50%" valign=top>
version [rev1][date1]
[if-any tag1]<br>Tag: [tag1][end]
</th>
<th width="50%" valign=top>
version [rev2][date2]
[if-any tag2]<br>Tag: [tag2][end]
</th>
</tr>
[for changes]
[is changes.type "header"]
<tr bgcolor="#99cccc"><td width="50%">
<table width="100%" border=1 cellpadding=5><tr>
<td><b>Line [changes.line1]</b>&nbsp;<font
size="-1">[changes.extra]</font></td>
</tr></table></td><td width="50%">
<table width="100%" border=1 cellpadding=5><tr>
<td><b>Line [changes.line2]</b>&nbsp;<font
size="-1">[changes.extra]</font></td>
</tr></table>
</td></tr>
[else]
[is changes.type "add"]
<tr>
<td bgcolor="#cccccc">&nbsp;</td>
<td bgcolor="#aaffaa">[changes.right]</td>
</tr>
[else]
[is changes.type "remove"]
<tr>
<td bgcolor="#ffaaaa">[changes.left]</td>
<td bgcolor="#cccccc">&nbsp;</td>
</tr>
[else]
[is changes.type "change"]
<tr>
[if-any changes.have_left]<td bgcolor="#ffff77">[changes.left]</td>
[else]<td bgcolor="#eeee77">&nbsp;</td>[end]
[if-any changes.have_right]<td bgcolor="#ffff77">[changes.right]</td>
[else]<td bgcolor="#eeee77">&nbsp;</td>[end]
</tr>
[else]
[is changes.type "no-changes"]
<tr><td colspan=2>&nbsp;</td></tr>
<tr bgcolor="#cccccc"><td colspan=2 align=center>
<br><b>- No changes -</b><br>&nbsp;
</td></tr>
[else][# a line of context]
<tr><td>[changes.left]</td><td>[changes.right]</td></tr>
[end][end][end][end][end]
[end]
</table><br><hr noshade width="100%">
<table border=0 cellpadding=10><tr><td>
<table border=1><tr><td>Legend:<br>
<table border=0 cellspacing=0 cellpadding=1>
<tr>
<td align=center bgcolor="#ffaaaa">Removed from v.[rev1]</td>
<td bgcolor="#cccccc">&nbsp;</td>
</tr>
<tr bgcolor="#ffff77"><td align=center colspan=2>changed lines</td></tr>
<tr>
<td bgcolor="#cccccc">&nbsp;</td>
<td align=center bgcolor="#aaffaa">Added in v.[rev2]</td>
</tr>
</table></td></tr></table></td>
<td><form method="GET" action="[request.url]">
[hidden_values]
<select name="diff_format" onchange="submit()">
<option value="h" [is diff_format "h"]selected[end]>Colored Diff</option>
<option value="l" [is diff_format "l"]selected[end]>Long Colored Diff</option>
<option value="u" [is diff_format "u"]selected[end]>Unidiff</option>
<option value="c" [is diff_format "c"]selected[end]>Context Diff</option>
<option value="s" [is diff_format "s"]selected[end]>Side by Side</option>
</select>
<input type=submit value="Show">
</form></td></tr>
</table>
[include "footer.ezt"]

12
templates/graph.ezt Normal file
View File

@ -0,0 +1,12 @@
[include "header.ezt"]
<center>
<h1>Revision graph of [request.where]</h1>
[imagemap]
<img border="0" usemap="#MyMapName"
src="[request.url]?graph=[rev]&makeimage=1[request.amp_query]"
alt="Revisions of [request.where]">
</center>
[include "footer.ezt"]

25
templates/header.ezt Normal file
View File

@ -0,0 +1,25 @@
<html><head>
<!-- ViewCVS -- http://viewcvs.sourceforge.net/
by Greg Stein -- mailto:gstein@lyra.org
-->
[### NOTE: the "diff" is the TITLE param to navigate_header() ]
<title>[path]/[filename] - [title] - [rev]</title>
</head>
<body bgcolor="#eeeeee">
<table width="100%" border=0 cellspacing=0 cellpadding=1 bgcolor="#9999ee">
<tr valign=bottom>
<td>
<a href="[file_url][qquery]#rev[rev]">
<img src="/icons/small/back.gif" alt="(file)" border=0 width=16 height=16>
</a>
<b>Return to
<a href="[file_url][qquery]#rev[rev]">[filename]</a>
CVS log</b>
<img src="/icons/small/text.gif" alt="(file)" border=0 width=16 height=16>
</td>
<td align=right>
<img src="/icons/small/dir.gif" alt="(dir)" border=0 width=16 height=16>
<b>Up to [nav_path]</b>
</td>
</tr>
</table>

49
templates/markup.ezt Normal file
View File

@ -0,0 +1,49 @@
[include "header.ezt"]
<hr noshade>
<table width="100%"><tr><td bgcolor="#ffffff">
File: [nav_path]
(<a href="[href]" target="cvs_checkout"
onClick="window.open('about:blank','cvs_checkout',
'resizeable=1,scrollbars=1[is mime_type "text/html"],status,toolbar[end]');"
><b>download</b></a>)
[is mime_type "text/plain"]
[else]
/
(<a href="[text_href]" target="cvs_checkout"
onClick="window.open('about:blank', 'cvs_checkout',
'resizeable=1,scrollbars=1')"><b>as text</b></a>)
[end]
<br>
[if-any log]
Revision: <b>[rev]</b>[if-any vendor_branch] <i>(vendor branch)</i>[end],
<i>[utc_date]</i> ([ago] ago) by <i>[author]</i>
[if-any branches]
<br>Branch: <b>[branches]</b>
[end]
[if-any tags]
<br>CVS Tags: <b>[tags]</b>
[end]
[if-any branch_points]
<br>Branch point for: <b>[branch_points]</b>
[end]
[if-any prev]
[if-any changed]
<br>Changes since <b>[prev]: [changed] lines</b>
[end]
[end]
[is state "dead"]
<br><b><i>FILE REMOVED</i></b>
[end]
<pre>[log]</pre>
[else]
Revision: <b>[rev]</b><br>
[if-any tag]
Tag: <b>[tag]</b><br>
[end]
[end]
</td></tr></table>
<hr noshade>

View File

@ -42,15 +42,101 @@
<ul>
<li><a href="#from7">Upgrading from ViewCVS 0.7 or earlier</a></li>
<li><a href="#from8">Upgrading from ViewCVS 0.8</a></li>
</ul>
<hr>
<h2><a name="from8">Upgrading from ViewCVS 0.8</a></h2>
<p>
This section discusses how to upgrade ViewCVS 0.8 to version
0.9 or a later version of the software.
</p>
<h3>Templates</h3>
<p>
More templates were introduced in version 0.8 of the software,
which made many of the configuration options obsolete. This
section covers which options were removed. If you made any
changes to these options, then you will need to make
corresponding changes in the templates.
</p>
<blockquote>
<dl>
<dt>
Colors:
<strong>diff_heading</strong>,
<strong>diff_empty</strong>,
<strong>diff_remove</strong>,
<strong>diff_change</strong>,
<strong>diff_add</strong>,
and <strong>diff_dark_change</strong>
</dt>
<dd>
These options have been incorporated into the
<code>diff.ezt</code> template.
<p></p>
</dd>
<dt><strong>markup_log</strong></dt>
<dd>
This option has been incorporated into the
<code>markup.ezt</code> template.
<p></p>
</dd>
<dt>Colors: <strong>nav_header</strong>
and <strong>alt_background</strong></dt>
<dd>
These options have been incorporated into the
<code>header.ezt</code> template.
<p></p>
</dd>
<dt>
Images:
<strong>back_icon</strong>,
<strong>dir_icon</strong>,
and <strong>file_icon</strong>
</dt>
<dd>
These options have been incorporated into the
<code>directory.ezt</code>, <code>header.ezt</code>,
<code>log.ezt</code>, <code>log_table.ezt</code>, and
<code>query.ezt</code> templates.
<p></p>
</dd>
<dt><strong>use_java_script</strong>
and <strong>open_extern_window</strong></dt>
<dd>
The templates now use JavaScript in all applicable places,
and open external windows for most downloading and viewing
of files. If you wish to not use JavaScript and/or external
windows, then remove the feature(s) from the templates.
<p></p>
</dd>
</dl>
</blockquote>
<hr>
<h2><a name="from7">Upgrading from ViewCVS 0.7 or earlier</a></h2>
<p>
This section discusses how to upgrade ViewCVS 0.7 or earlier to
0.8 or a later version of the software.
</p>
<p>
<strong>NOTE:</strong> these changes will bring you up to the
requirements of version 0.8. You must also follow the directions
for <a href="#from8">upgrading from 0.8</a>.
</p>
<h3>Templates</h3>
<p>
@ -164,6 +250,24 @@
<p></p>
</dd>
<dt>Colors: <strong>text</strong>
and <strong>background</strong></dt>
<dd>
These options have been incorporated into the
<code>directory.ezt</code>, <code>log.ezt</code>, and
<code>log_table.ezt</code> templates.
<p></p>
</dd>
<dt><strong>main_title</strong></dt>
<dd>
This options has been incorporated into the
<code>directory.ezt</code> template.
<p></p>
</dd>
</dl>
</blockquote>
@ -172,7 +276,7 @@
<address><a href="mailto:viewcvs@lyra.org">ViewCVS Users Group</a></address>
<!-- Created: Mon Sep 24 04:23:53 PDT 2001 -->
<!-- hhmts start -->
Last modified: Mon Dec 10 02:06:31 PST 2001
Last modified: Mon Dec 17 00:54:43 PST 2001
<!-- hhmts end -->
</body>
</html>