2001-08-21 23:24:40 +04:00
|
|
|
#!/usr/bin/python
|
2000-06-11 20:47:37 +04:00
|
|
|
# -*- Mode: python -*-
|
|
|
|
#
|
2001-08-21 23:24:40 +04:00
|
|
|
# Copyright (C) 2000 The ViewCVS Group. All Rights Reserved.
|
2000-06-11 20:47:37 +04:00
|
|
|
#
|
|
|
|
# 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 ViewCVS
|
2001-08-21 23:24:40 +04:00
|
|
|
# distribution or at http://www.lyra.org/viewcvs/license-1.html.
|
2000-06-11 20:47:37 +04:00
|
|
|
#
|
|
|
|
# Contact information:
|
|
|
|
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
|
2001-08-21 23:24:40 +04:00
|
|
|
# gstein@lyra.org, http://www.lyra.org/viewcvs/
|
|
|
|
#
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# CGI script to process and display queries to CVSdb
|
|
|
|
#
|
|
|
|
# This script is part of the ViewCVS package. More information can be
|
|
|
|
# found at http://www.lyra.org/viewcvs/.
|
2000-06-11 20:47:37 +04:00
|
|
|
#
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
#########################################################################
|
|
|
|
#
|
|
|
|
# INSTALL-TIME CONFIGURATION
|
|
|
|
#
|
|
|
|
# These values will be set during the installation process. During
|
|
|
|
# development, they will remain None.
|
|
|
|
#
|
|
|
|
|
|
|
|
CONF_PATHNAME = None
|
|
|
|
|
|
|
|
#########################################################################
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import string
|
|
|
|
import cgi
|
2000-06-11 20:47:37 +04:00
|
|
|
import time
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
import cvsdb
|
|
|
|
import viewcvs
|
2001-10-25 13:21:47 +04:00
|
|
|
import ezt
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
class FormData:
|
|
|
|
def __init__(self, form):
|
|
|
|
self.valid = 0
|
|
|
|
|
|
|
|
self.repository = ""
|
|
|
|
self.branch = ""
|
|
|
|
self.directory = ""
|
|
|
|
self.file = ""
|
|
|
|
self.who = ""
|
|
|
|
self.sortby = ""
|
|
|
|
self.date = ""
|
|
|
|
self.hours = 0
|
|
|
|
|
|
|
|
self.decode_thyself(form)
|
|
|
|
|
|
|
|
def decode_thyself(self, form):
|
|
|
|
try:
|
|
|
|
self.repository = string.strip(form["repository"].value)
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.valid = 1
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.branch = string.strip(form["branch"].value)
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.valid = 1
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.directory = string.strip(form["directory"].value)
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.valid = 1
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.file = string.strip(form["file"].value)
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.valid = 1
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.who = string.strip(form["who"].value)
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.valid = 1
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.sortby = string.strip(form["sortby"].value)
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.date = string.strip(form["date"].value)
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
try:
|
|
|
|
self.hours = int(form["hours"].value)
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
except TypeError:
|
|
|
|
pass
|
|
|
|
except ValueError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
self.valid = 1
|
|
|
|
|
|
|
|
## returns a tuple-list (mod-str, string)
|
|
|
|
def listparse_string(str):
|
|
|
|
return_list = []
|
|
|
|
|
|
|
|
cmd = ""
|
|
|
|
temp = ""
|
|
|
|
escaped = 0
|
|
|
|
state = "eat leading whitespace"
|
|
|
|
|
|
|
|
for c in str:
|
|
|
|
## handle escaped charactors
|
|
|
|
if not escaped and c == "\\":
|
|
|
|
escaped = 1
|
|
|
|
continue
|
|
|
|
|
|
|
|
## strip leading white space
|
|
|
|
if state == "eat leading whitespace":
|
|
|
|
if c in string.whitespace:
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
state = "get command or data"
|
|
|
|
|
|
|
|
## parse to '"' or ","
|
|
|
|
if state == "get command or data":
|
|
|
|
|
|
|
|
## just add escaped charactors
|
|
|
|
if escaped:
|
|
|
|
escaped = 0
|
|
|
|
temp = temp + c
|
|
|
|
continue
|
|
|
|
|
|
|
|
## the data is in quotes after the command
|
|
|
|
elif c == "\"":
|
|
|
|
cmd = temp
|
|
|
|
temp = ""
|
|
|
|
state = "get quoted data"
|
|
|
|
continue
|
|
|
|
|
|
|
|
## this tells us there was no quoted data, therefore no
|
|
|
|
## command; add the command and start over
|
|
|
|
elif c == ",":
|
|
|
|
## strip ending whitespace on un-quoted data
|
|
|
|
temp = string.rstrip(temp)
|
|
|
|
return_list.append( ("", temp) )
|
|
|
|
temp = ""
|
|
|
|
state = "eat leading whitespace"
|
|
|
|
continue
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
## record the data
|
|
|
|
else:
|
|
|
|
temp = temp + c
|
|
|
|
continue
|
|
|
|
|
|
|
|
## parse until ending '"'
|
|
|
|
if state == "get quoted data":
|
|
|
|
|
|
|
|
## just add escaped charactors
|
|
|
|
if escaped:
|
|
|
|
escaped = 0
|
|
|
|
temp = temp + c
|
|
|
|
continue
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
## look for ending '"'
|
|
|
|
elif c == "\"":
|
|
|
|
return_list.append( (cmd, temp) )
|
|
|
|
cmd = ""
|
|
|
|
temp = ""
|
|
|
|
state = "eat comma after quotes"
|
|
|
|
continue
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
## record the data
|
|
|
|
else:
|
|
|
|
temp = temp + c
|
|
|
|
continue
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
## parse until ","
|
|
|
|
if state == "eat comma after quotes":
|
|
|
|
if c in string.whitespace:
|
|
|
|
continue
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
elif c == ",":
|
|
|
|
state = "eat leading whitespace"
|
|
|
|
continue
|
|
|
|
|
|
|
|
else:
|
|
|
|
print "format error"
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
if cmd or temp:
|
|
|
|
return_list.append((cmd, temp))
|
|
|
|
|
|
|
|
return return_list
|
|
|
|
|
|
|
|
def decode_command(cmd):
|
|
|
|
if cmd == "r":
|
|
|
|
return "regex"
|
|
|
|
elif cmd == "l":
|
|
|
|
return "like"
|
|
|
|
else:
|
|
|
|
return "exact"
|
|
|
|
|
|
|
|
def form_to_cvsdb_query(form_data):
|
|
|
|
query = cvsdb.CreateCheckinQuery()
|
|
|
|
|
|
|
|
if form_data.repository:
|
|
|
|
for cmd, str in listparse_string(form_data.repository):
|
|
|
|
cmd = decode_command(cmd)
|
|
|
|
query.SetRepository(str, cmd)
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
if form_data.branch:
|
|
|
|
for cmd, str in listparse_string(form_data.branch):
|
|
|
|
cmd = decode_command(cmd)
|
|
|
|
query.SetBranch(str, cmd)
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
if form_data.directory:
|
|
|
|
for cmd, str in listparse_string(form_data.directory):
|
|
|
|
cmd = decode_command(cmd)
|
|
|
|
query.SetDirectory(str, cmd)
|
|
|
|
|
|
|
|
if form_data.file:
|
|
|
|
for cmd, str in listparse_string(form_data.file):
|
|
|
|
cmd = decode_command(cmd)
|
|
|
|
query.SetFile(str, cmd)
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
if form_data.who:
|
|
|
|
for cmd, str in listparse_string(form_data.who):
|
|
|
|
cmd = decode_command(cmd)
|
|
|
|
query.SetAuthor(str, cmd)
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
if form_data.sortby == "author":
|
|
|
|
query.SetSortMethod("author")
|
|
|
|
elif form_data.sortby == "file":
|
|
|
|
query.SetSortMethod("file")
|
|
|
|
else:
|
|
|
|
query.SetSortMethod("date")
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
if form_data.date:
|
|
|
|
if form_data.date == "hours" and form_data.hours:
|
|
|
|
query.SetFromDateHoursAgo(form_data.hours)
|
|
|
|
elif form_data.date == "day":
|
|
|
|
query.SetFromDateDaysAgo(1)
|
|
|
|
elif form_data.date == "week":
|
|
|
|
query.SetFromDateDaysAgo(7)
|
|
|
|
elif form_data.date == "month":
|
|
|
|
query.SetFromDateDaysAgo(31)
|
|
|
|
|
|
|
|
return query
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
def cvsroot_name_from_path(cvsroot):
|
|
|
|
## we need to resolve the cvsroot path from the database
|
|
|
|
## to the name given to it in the viewcvs.conf file
|
2001-11-18 14:11:38 +03:00
|
|
|
for key, value in cfg.general.cvs_roots.items():
|
2001-08-21 23:24:40 +04:00
|
|
|
if value == cvsroot:
|
2001-11-18 14:11:38 +03:00
|
|
|
return key
|
|
|
|
|
|
|
|
return None
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-11-18 13:34:35 +03:00
|
|
|
def build_commit(desc, files):
|
|
|
|
ob = _item(num_files=len(files), files=[])
|
2001-08-21 23:24:40 +04:00
|
|
|
|
2001-11-18 13:34:35 +03:00
|
|
|
if desc:
|
|
|
|
ob.desc = string.replace(cgi.escape(desc), '\n', '<br>')
|
|
|
|
else:
|
|
|
|
ob.desc = ' '
|
|
|
|
|
|
|
|
for commit in files:
|
2001-08-21 23:24:40 +04:00
|
|
|
ctime = commit.GetTime()
|
|
|
|
if not ctime:
|
|
|
|
ctime = " ";
|
|
|
|
else:
|
|
|
|
ctime = time.strftime("%y/%m/%d %H:%M", time.localtime(ctime))
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
## make the file link
|
|
|
|
file = os.path.join(commit.GetDirectory(), commit.GetFile())
|
|
|
|
file_full_path = os.path.join(commit.GetRepository(), file)
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
## if we couldn't find the cvsroot path configured in the
|
|
|
|
## viewcvs.conf file, then don't make the link
|
|
|
|
cvsroot_name = cvsroot_name_from_path(commit.GetRepository())
|
|
|
|
if cvsroot_name:
|
2001-11-18 14:11:38 +03:00
|
|
|
flink = '<a href="viewcvs.cgi/%s?cvsroot=%s">%s</a>' \
|
|
|
|
% (file, cvsroot_name, file_full_path)
|
2001-08-21 23:24:40 +04:00
|
|
|
else:
|
|
|
|
flink = file_full_path
|
2000-06-11 20:47:37 +04:00
|
|
|
|
2001-11-18 13:34:35 +03:00
|
|
|
ob.files.append(_item(date=ctime,
|
2001-11-18 14:11:38 +03:00
|
|
|
author=commit.GetAuthor(),
|
2001-11-18 13:34:35 +03:00
|
|
|
link=flink,
|
2001-11-18 14:11:38 +03:00
|
|
|
rev=commit.GetRevision(),
|
|
|
|
branch=commit.GetBranch(),
|
|
|
|
plus=int(commit.GetPlusCount()),
|
|
|
|
minus=int(commit.GetMinusCount()),
|
|
|
|
))
|
2001-11-18 13:34:35 +03:00
|
|
|
|
|
|
|
return ob
|
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
def run_query(form_data):
|
|
|
|
query = form_to_cvsdb_query(form_data)
|
|
|
|
db = cvsdb.ConnectDatabaseReadOnly()
|
|
|
|
db.RunQuery(query)
|
|
|
|
|
2001-11-18 13:34:35 +03:00
|
|
|
if not query.commit_list:
|
|
|
|
return [ ]
|
|
|
|
|
|
|
|
commits = [ ]
|
|
|
|
files = [ ]
|
|
|
|
|
|
|
|
current_desc = query.commit_list[0].GetDescription()
|
2001-08-21 23:24:40 +04:00
|
|
|
for commit in query.commit_list:
|
|
|
|
desc = commit.GetDescription()
|
|
|
|
if current_desc == desc:
|
2001-11-18 13:34:35 +03:00
|
|
|
files.append(commit)
|
2001-08-21 23:24:40 +04:00
|
|
|
continue
|
|
|
|
|
2001-11-18 13:34:35 +03:00
|
|
|
commits.append(build_commit(current_desc, files))
|
|
|
|
|
|
|
|
files = [ commit ]
|
2001-08-21 23:24:40 +04:00
|
|
|
current_desc = desc
|
2001-11-18 13:34:35 +03:00
|
|
|
|
|
|
|
## add the last file group to the commit list
|
|
|
|
commits.append(build_commit(current_desc, files))
|
|
|
|
|
|
|
|
return commits
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
def handle_config():
|
|
|
|
viewcvs.handle_config()
|
|
|
|
global cfg
|
|
|
|
cfg = viewcvs.cfg
|
|
|
|
|
|
|
|
def main():
|
|
|
|
handle_config()
|
|
|
|
|
|
|
|
form = cgi.FieldStorage()
|
|
|
|
form_data = FormData(form)
|
2001-10-25 13:21:47 +04:00
|
|
|
|
2001-11-18 13:34:35 +03:00
|
|
|
if form_data.valid:
|
|
|
|
commits = run_query(form_data)
|
2001-11-18 14:11:38 +03:00
|
|
|
query = None
|
2001-11-18 13:34:35 +03:00
|
|
|
else:
|
|
|
|
commits = [ ]
|
2001-11-18 14:11:38 +03:00
|
|
|
query = 'skipped'
|
2001-11-18 13:34:35 +03:00
|
|
|
|
2001-10-25 13:21:47 +04:00
|
|
|
data = {
|
|
|
|
'cfg' : cfg,
|
|
|
|
'address' : cfg.general.address,
|
|
|
|
'vsn' : viewcvs.__version__,
|
|
|
|
|
|
|
|
'repository' : cgi.escape(form_data.repository, 1),
|
|
|
|
'branch' : cgi.escape(form_data.branch, 1),
|
|
|
|
'directory' : cgi.escape(form_data.directory, 1),
|
|
|
|
'file' : cgi.escape(form_data.file, 1),
|
|
|
|
'who' : cgi.escape(form_data.who, 1),
|
|
|
|
|
|
|
|
'sortby' : form_data.sortby,
|
|
|
|
'date' : form_data.date,
|
2001-11-18 13:34:35 +03:00
|
|
|
|
2001-11-18 14:11:38 +03:00
|
|
|
'query' : query,
|
2001-11-18 13:34:35 +03:00
|
|
|
'commits' : commits,
|
|
|
|
'num_commits' : len(commits),
|
2001-10-25 13:21:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if form_data.hours:
|
|
|
|
data['hours'] = form_data.hours
|
|
|
|
else:
|
|
|
|
data['hours'] = 2
|
|
|
|
|
|
|
|
template = ezt.Template()
|
2001-11-19 14:15:18 +03:00
|
|
|
template.parse_file(os.path.join(viewcvs.g_install_dir,
|
2001-10-25 13:21:47 +04:00
|
|
|
cfg.templates.query))
|
|
|
|
|
|
|
|
viewcvs.http_header()
|
|
|
|
|
|
|
|
# generate the page
|
|
|
|
template.generate(sys.stdout, data)
|
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
def run_cgi():
|
2001-10-25 13:21:47 +04:00
|
|
|
|
|
|
|
### be nice to share all this logic with viewcvs.run_cgi
|
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
try:
|
|
|
|
main()
|
|
|
|
except SystemExit, e:
|
|
|
|
# don't catch SystemExit (caused by sys.exit()). propagate the exit code
|
|
|
|
sys.exit(e[0])
|
|
|
|
except:
|
|
|
|
info = sys.exc_info()
|
2001-10-25 13:21:47 +04:00
|
|
|
viewcvs.http_header()
|
|
|
|
print '<html><head><title>Python Exception Occurred</title></head>'
|
|
|
|
print '<body bgcolor=white><h1>Python Exception Occurred</h1>'
|
2001-08-21 23:24:40 +04:00
|
|
|
import traceback
|
|
|
|
lines = apply(traceback.format_exception, info)
|
|
|
|
print '<pre>'
|
|
|
|
print cgi.escape(string.join(lines, ''))
|
|
|
|
print '</pre>'
|
|
|
|
viewcvs.html_footer()
|
2001-11-18 13:34:35 +03:00
|
|
|
|
|
|
|
|
|
|
|
class _item:
|
|
|
|
def __init__(self, **kw):
|
|
|
|
vars(self).update(kw)
|