Fri Jul 1 02:55:14 UTC 2005 Alberto Bertogli * Initial import. diff -rN -u old-darcsweb/README new-darcsweb/README --- old-darcsweb/README 1970-01-01 00:00:00.000000000 +0000 +++ new-darcsweb/README 2015-04-18 08:11:00.000000000 +0000 @@ -0,0 +1,16 @@ + +darcsweb - A web interface for darcs +Alberto Bertogli (albertogli@telpin.com.ar) +------------------------------------------- + +This is a very simple web interface for darcs, inspired in gitweb (written by +Kay Sievers and Christian Gierke). + +To configure, copy the "config.py.sample" file to "config.py" and edit it; you +will configure your repositories there. Then just browse to the cgi file. + +If you have any question, suggestions or comments, please let me know. + +Thanks, + Alberto + diff -rN -u old-darcsweb/TODO new-darcsweb/TODO --- old-darcsweb/TODO 1970-01-01 00:00:00.000000000 +0000 +++ new-darcsweb/TODO 2015-04-18 08:11:00.000000000 +0000 @@ -0,0 +1,7 @@ + +* export plains (done, needs to be added to the interface) +* be able to see a file tree at any point of time/patchflow + * we need darcs support for this, which is currently not available + * when this is done, show tags and allow displaying of blobs +* annotate + diff -rN -u old-darcsweb/config.py.sample new-darcsweb/config.py.sample --- old-darcsweb/config.py.sample 1970-01-01 00:00:00.000000000 +0000 +++ new-darcsweb/config.py.sample 2015-04-18 08:11:00.000000000 +0000 @@ -0,0 +1,54 @@ + +# base configuration, common to all repos +class base: + # this script's name, usually just "darcsweb.cgi" unless you rename it + myname = "darcsweb.cgi" + + # our url, used only to generate RSS links, without the script name + myurl = "http://albertito.homeip.net:8026/darcsweb" + + # location of the darcs logo + darcslogo = "darcs.png" + + # location of the darcs favicon + darcsfav = "minidarcs.png" + + # the CSS file to use + cssfile = 'style.css' + + +# +# From now on, every class is a repo configuration, with the same format +# There are no restrictions on the class' name, except that it can't be named +# "base" (because it's the name of the one avobe). +# + +class repo1: + # the descriptive name + reponame = 'repo1' + + # a brief description + repodesc = 'Example repository' + + # the real path to the repository + repodir = '/usr/src/repo1' + + # an url so people know where to do "darcs get" from + repourl = 'http://auriga.wearlab.de/~alb/repos/repo1' + + # the encoding used in the repo + # NOTE: if you use utf8, you _must_ write 'utf8' (and not the variants + # like 'utf-8' or 'UTF8') if you expect darcsweb to work properly. + # This is because to workaround a bug in darcs we need to do some + # codec mangling and it needs special cases for UTF8. + repoencoding = "latin1" + + +class repo2: + reponame = 'repo2' + repodesc = 'Second example repository' + repodir = '/usr/src/repo2' + repourl = 'http://auriga.wearlab.de/~alb/repos/repo2' + repoencoding = 'latin1' + + Binary files old-darcsweb/darcs.png and new-darcsweb/darcs.png differ diff -rN -u old-darcsweb/darcsweb.cgi new-darcsweb/darcsweb.cgi --- old-darcsweb/darcsweb.cgi 1970-01-01 00:00:00.000000000 +0000 +++ new-darcsweb/darcsweb.cgi 2015-04-18 08:11:00.000000000 +0000 @@ -0,0 +1,1237 @@ +#!/usr/bin/env python + +""" +darcsweb - A web interface for darcs +Alberto Bertogli (albertogli@telpin.com.ar) + +Inspired on gitweb (as of 28/Jun/2005), which is written by Kay Sievers + and Christian Gierke +""" + +import sys +import os +import string +import time +import stat +import cgi +import cgitb; cgitb.enable() +import xml.sax +from xml.sax.saxutils import escape + + +# empty configuration class, we will fill it in later depending on the repo +class config: + pass + + +# +# utility functions +# + +def filter_num(s): + l = [c for c in s if c in string.digits] + return string.join(l, "") + + +allowed_alphanum = string.ascii_letters + string.digits +def filter_an(s): + l = [c for c in s if c in allowed_alphanum] + return string.join(l, "") + + +allowed_in_hash = string.ascii_letters + string.digits + '-.' +def filter_hash(s): + l = [c for c in s if c in allowed_in_hash] + return string.join(l, "") + + +def filter_file(s): + if '..' in s: + raise 'FilterFile FAILED' + if s == '/': + return s + + # remove extra "/"s + r = s[0] + last = s[0] + for c in s[1:]: + if c == last and c == '/': + continue + r += c + last = c + return r + + +def printd(*params): + print string.join(params), '
' + + +# I _hate_ this. +def fixu8(s): + openpos = s.find('[_') + if openpos < 0: + # small optimization to avoid the conversion to utf8 and + # entering the loop + return s.decode(config.repoencoding).encode('utf8') + + s = s.encode(config.repoencoding).decode('raw_unicode_escape') + while openpos >= 0: + closepos = s.find('_]', openpos) + if closepos < 0: + # not closed, probably just luck + break + + # middle should be something like 'c3', so we get it by + # removing the first three characters ("[_\") + middle = s[openpos + 3:closepos] + if len(middle) == 2: + # now we turn middle into the character "\xc3" + char = chr(int(middle, 16)) + + # finally, replace s with our new improved string, and repeat + # the ugly procedure + char = char.decode('raw_unicode_escape') + mn = '[_\\' + middle + '_]' + s = s.replace(mn, char, 1) + openpos = s.find('[_', openpos + 1) + + if config.repoencoding != 'utf8': + s = s.encode('utf8') + else: + s = s.encode('raw_unicode_escape', 'replace') + return s + + +def how_old(epoch): + age = int(time.time()) - int(epoch) + if age > 60*60*24*365*2: + s = str(age/60/60/24/365) + s += " years ago" + elif age > 60*60*24*(365/12)*2: + s = str(age/60/60/24/(365/12)) + s += " month ago" + elif age > 60*60*24*7*2: + s = str(age/60/60/24/7) + s += " weeks ago" + elif age > 60*60*24*2: + s = str(age/60/60/24) + s += " days ago" + elif age > 60*60*2: + s = str(age/60/60) + s += " hours ago" + elif age > 60*2: + s = str(age/60) + s += " minutes ago" + elif age > 2: + s = str(age) + s += " seconds ago" + else: + s = "right now" + return s + +def shorten_str(s, max = 60): + if len(s) > max: + s = s[:max - 4] + ' ...' + return s + +def fperms(fname): + m = os.stat(fname)[stat.ST_MODE] + m = m & 0777 + s = [] + if os.path.isdir(fname): s.append('d') + else: s.append('-') + + if m & 0400: s.append('r') + else: s.append('-') + if m & 0200: s.append('w') + else: s.append('-') + if m & 0100: s.append('x') + else: s.append('-') + + if m & 0040: s.append('r') + else: s.append('-') + if m & 0020: s.append('w') + else: s.append('-') + if m & 0010: s.append('x') + else: s.append('-') + + if m & 0004: s.append('r') + else: s.append('-') + if m & 0002: s.append('w') + else: s.append('-') + if m & 0001: s.append('x') + else: s.append('-') + + return string.join(s, '') + + +def isbinary(fname): + import re + bins = open(config.repodir + '/_darcs/prefs/binaries').readlines() + bins = [b[:-1] for b in bins if b and b[0] != '#'] + for b in bins: + if re.compile(b).search(fname): + return 1 + return 0 + + +# +# generic html functions +# + +def print_header(): + print "Content-type: text/html; charset=utf-8\n" + print """ + + + + + + + +darcs - %(reponame)s + + + + + + + +" + + +def print_footer(put_rss = 1): + print """ +\n\n" + + +def print_navbar(h = "", f = ""): + print """ +' + + +def print_plain_header(): + print "Content-type: text/plain\n" + + +# +# darcs repo manipulation +# + +def repo_get_owner(): + try: + fd = open(config.repodir + '/_darcs/prefs/author') + author = fd.readlines()[0].strip() + except: + author = "Unknown owner " + return author + +def run_darcs(params): + """Runs darcs on the repodir with the given params, return a file + object with its output.""" + os.chdir(config.repodir) + cmd = "darcs " + params + inf, outf = os.popen4(cmd, 'r') + return outf + + +class Patch: + "Represents a single patch/record" + def __init__(self): + self.hash = '' + self.author = '' + self.shortauthor = '' + self.date = 0 + self.local_date = 0 + self.name = '' + self.comment = '' + self.inverted = False; + self.adds = [] + self.removes = [] + self.modifies = {} + self.diradds = [] + self.dirremoves = [] + self.moves = {} + + def tostr(self): + s = "%s\n\tAuthor: %s\n\tDate: %s\n\tHash: %s\n" % \ + (self.name, self.author, self.date, self.hash) + return s + + def getdiff(self): + """Returns a list of lines from the diff -u corresponding with + the patch.""" + params = 'diff -u --match "hash %s"' % self.hash + f = run_darcs(params) + return f.readlines() + + +# patch parsing, we get them through "darcs changes --xml-output" +class BuildPatchList(xml.sax.handler.ContentHandler): + def __init__(self): + self.db = {} + self.list = [] + self.cur_hash = '' + self.cur_elem = None + self.cur_val = '' + self.cur_file = '' + + def startElement(self, name, attrs): + if name == 'patch': + p = Patch() + p.hash = fixu8(attrs.get('hash')) + + au = attrs.get('author', None) + p.author = fixu8(escape(au)) + if au.find('<') != -1: + au = au[:au.find('<')].strip() + p.shortauthor = fixu8(escape(au)) + + td = time.strptime(attrs.get('date', None), + "%Y%m%d%H%M%S") + p.date = time.mktime(td) + p.date_str = time.strftime("%a, %d %b %Y %H:%M:%S", td) + + td = time.strptime(attrs.get('local_date', None), + "%a %b %d %H:%M:%S %Z %Y") + p.local_date = time.mktime(td) + p.local_date_str = \ + time.strftime("%a, %d %b %Y %H:%M:%S", td) + + inverted = attrs.get('inverted', None) + if inverted and inverted == 'True': + p.inverted = True + + self.db[p.hash] = p + self.current = p.hash + self.list.append(p.hash) + elif name == 'name': + self.db[self.current].name = '' + self.cur_elem = 'name' + elif name == 'comment': + self.db[self.current].comment = '' + self.cur_elem = 'comment' + elif name == 'add_file': + self.cur_elem = 'add_file' + elif name == 'remove_file': + self.cur_elem = 'remove_file' + elif name == 'add_directory': + self.cur_elem = 'add_directory' + elif name == 'remove_directory': + self.cur_elem = 'remove_dir' + elif name == 'modify_file': + self.cur_elem = 'modify_file' + elif name == 'removed_lines': + if self.cur_val: + self.cur_file = fixu8(self.cur_val.strip()) + cf = self.cur_file + p = self.db[self.current] + # the current value holds the file name at this point + if not p.modifies.has_key(cf): + p.modifies[cf] = { '+': 0, '-': 0 } + p.modifies[cf]['-'] = int(attrs.get('num', None)) + elif name == 'added_lines': + if self.cur_val: + self.cur_file = fixu8(self.cur_val.strip()) + cf = self.cur_file + p = self.db[self.current] + if not p.modifies.has_key(cf): + p.modifies[cf] = { '+': 0, '-': 0 } + p.modifies[cf]['+'] = int(attrs.get('num', None)) + elif name == 'move': + src = fixu8(attrs.get('from', None)) + dst = fixu8(attrs.get('to', None)) + p = self.db[self.current] + p.moves[src] = dst + else: + self.cur_elem = None + + def characters(self, s): + if not self.cur_elem: + return + self.cur_val += s + + def endElement(self, name): + if name == 'name': + p = self.db[self.current] + p.name = fixu8(self.cur_val) + if p.inverted: + p.name = 'UNDO: ' + p.name + elif name == 'comment': + self.db[self.current].comment = fixu8(self.cur_val) + elif name == 'add_file': + scv = fixu8(self.cur_val.strip()) + self.db[self.current].adds.append(scv) + elif name == 'remove_file': + scv = fixu8(self.cur_val.strip()) + self.db[self.current].removes.append(scv) + elif name == 'add_directory': + scv = fixu8(self.cur_val.strip()) + self.db[self.current].diradds.append(scv) + elif name == 'remove_directory': + scv = fixu8(self.cur_val.strip()) + self.db[self.current].dirremoves.append(scv) + + elif name == 'modify_file': + if not self.cur_file: + # binary modification appear without a line + # change summary, so we add it manually here + f = fixu8(self.cur_val.strip()) + p = self.db[self.current] + p.modifies[f] = { '+': 0, '-': 0, 'b': 1 } + self.cur_file = '' + + self.cur_elem = None + self.cur_val = '' + + def get_list(self): + plist = [] + for h in self.list: + plist.append(self.db[h]) + return plist + + def get_db(self): + return self.db + + def get_list_db(self): + return (self.list, self.db) + +def get_changes_handler(params): + "Returns a handler for the changes output, run with the given params" + parser = xml.sax.make_parser() + handler = BuildPatchList() + parser.setContentHandler(handler) + + # get the xml output and parse it + xmlf = run_darcs("changes --xml-output " + params) + parser.parse(xmlf) + xmlf.close() + + return handler + +def get_last_patches(last = 15, topi = 0): + """Gets the last N patches from the repo, returns a patch list. If + "topi" is specified, then it will return the N patches that preceeded + the patch number topi in the list. It sounds messy but it's quite + simple. FIXME: there's probably a more efficient way of doing this.""" + toget = last + topi + handler = get_changes_handler("-s --last=%d" % toget) + + # return the list of all the patch objects + return handler.get_list()[topi:toget] + +def get_patch(hash): + handler = get_changes_handler('-s --match "hash %s"' % hash) + patch = handler.db[handler.list[0]] + return patch + +def get_file_diff(hash, fname): + return run_darcs("diff -u --match 'hash %s' '%s'" % (hash, fname)) + +def get_file_headdiff(hash, fname): + return run_darcs("diff -u --from-match 'hash %s' '%s'" % (hash, fname)) + +def get_patch_headdiff(hash): + return run_darcs("diff -u --from-match 'hash %s'" % hash) + + +# +# specific html functions +# + +def print_diff(dsrc): + for l in dsrc: + l = l.decode(config.repoencoding, 'replace').encode('utf-8') + + # remove the trailing newline + if len(l) > 1: + l = l[:-1] + + if l.startswith('diff'): + # file lines, they have their own class + print '
%s
' % l + continue + + color = "" + if l[0] == '+': + color = 'style="color:#008800;"' + elif l[0] == '-': + color = 'style="color:#cc0000;"' + elif l[0] == '@': + color = 'style="color:#990099;"' + elif l.startswith('Files'): + # binary differences + color = 'style="color:#666;"' + print '
' % color + escape(l) + '
' + + +def print_shortlog(last = 50, topi = 0): + ps = get_last_patches(last, topi) + print '
shortlog
' \ + % config.myreponame + print '' + + if topi != 0: + # put a link to the previous page + ntopi = topi - last + if ntopi < 0: + ntopi = 0 + print '' % \ + (config.myreponame, ntopi) + + alt = False + for p in ps: + if alt: + print '' + else: + print '' + alt = not alt + + print """ + + + + + + """ % { + 'age': how_old(p.local_date), + 'author': p.shortauthor, + 'myrname': config.myreponame, + 'hash': p.hash, + 'name': shorten_str(p.name), + } + print "" + + if len(ps) >= last: + # only show if we've not shown them all already + print '' % \ + (config.myreponame, topi + last) + print "
...
%(age)s%(author)s + %(name)s +
...
" + + +def print_log(last = 50, topi = 0): + ps = get_last_patches(last, topi) + + if topi != 0: + # put a link to the previous page + ntopi = topi - last + if ntopi < 0: + ntopi = 0 + print '

<- Prev

' % \ + (config.myreponame, ntopi) + + for p in ps: + if p.comment: + fmt_comment = p.comment.replace('\n', '
') + '\n' + fmt_comment += '

' + else: + fmt_comment = '' + print """ +

+ %(age)s%(desc)s +
+
+ + %(author)s [%(date)s]
+
+
+ %(desc)s
+
+ %(comment)s +
+ + """ % { + 'myreponame': config.myreponame, + 'age': how_old(p.local_date), + 'date': p.local_date_str, + 'author': p.shortauthor, + 'hash': p.hash, + 'desc': shorten_str(p.name), + 'comment': fmt_comment + } + + if len(ps) >= last: + # only show if we've not shown them all already + print '

Next ->

' % \ + (config.myreponame, topi + last) + + +def print_blob(fname): + print '

%s
' % fname + print '
' + if isbinary(fname): + print """ +This is a binary file and it's contents will not be displayed. +
+ """ + return + + f = open(config.repodir + '/_darcs/current/' + fname, 'r') + count = 1 + for l in f: + l = fixu8(escape(l)) + if l and l[-1] == '\n': + l = l[:-1] + + print """ +
%(c)4d %(l)s
+ """ % { + 'c': count, + 'l': l + } + count += 1 + print '' + + +# +# available actions +# + +def do_summary(): + print_header() + print_navbar() + owner = repo_get_owner() + + # we should optimize this, it's a pity to go in such a mess for just + # one hash + ps = get_last_patches(1) + + print '
 
' + print '' + print ' ' % config.repodesc + print ' ' % owner + print ' ' % \ + ps[0].local_date_str + print ' ' %\ + { 'url': config.repourl } + print '
description%s
owner%s
last change%s
url%(url)s
' + + print_shortlog(15) + print_footer() + + +def do_commitdiff(phash): + print_header() + print_navbar(h = phash) + p = get_patch(phash) + print """ +
+ %(name)s +
+ """ % { + 'myreponame': config.myreponame, + 'hash': p.hash, + 'name': p.name, + } + + dsrc = p.getdiff() + print_diff(dsrc) + print_footer() + + +def do_headdiff(phash): + print_header() + print_navbar(h = phash) + p = get_patch(phash) + print """ +
+ + %(name)s --> to head +
+ """ % { + 'myreponame': config.myreponame, + 'hash': p.hash, + 'name': p.name, + } + + dsrc = get_patch_headdiff(phash) + print_diff(dsrc) + print_footer() + + +def do_filediff(phash, fname): + print_header() + print_navbar(h = phash, f = fname) + p = get_patch(phash) + dsrc = get_file_diff(phash, fname) + print """ +
+ %(name)s +
+
%(fname)s
+ """ % { + 'myreponame': config.myreponame, + 'hash': p.hash, + 'name': p.name, + 'fname': fname, + } + + print_diff(dsrc) + print_footer() + + +def do_file_headdiff(phash, fname): + print_header() + print_navbar(h = phash, f = fname) + p = get_patch(phash) + dsrc = get_file_headdiff(phash, fname) + print """ +
+ + %(name)s --> to head +
+
%(fname)s
+ """ % { + 'myreponame': config.myreponame, + 'hash': p.hash, + 'name': p.name, + 'fname': fname, + } + + print_diff(dsrc) + print_footer() + + +def do_plainfilediff(phash, fname): + print_plain_header() + dsrc = get_file_diff(phash, fname) + for l in dsrc: + sys.stdout.write(l) + + +def do_commit(phash): + print_header() + print_navbar(h = phash) + p = get_patch(phash) + + print """ +
+ %(name)s +
+ +
+ + + + + +
author%(author)s
local date%(local_date)s
date%(date)s
hash%(hash)s
+
+ """ % { + 'myreponame': config.myreponame, + 'author': p.author, + 'local_date': p.local_date_str, + 'date': p.date_str, + 'hash': p.hash, + 'name': p.name, + } + if p.comment: + c = p.comment.replace('\n', '
') + print '
', c, '
' + + changed = p.adds + p.removes + p.modifies.keys() + p.moves.keys() + \ + p.diradds + p.dirremoves + + if changed or p.moves: + n = len(changed) + print '
%d file(s) changed:
' % n + + print '' + changed.sort() + alt = False + for f in changed: + if alt: + print '' + else: + print '' + alt = not alt + + show_diff = 1 + if p.moves.has_key(f): + # don't show diffs for moves, they're broken as of + # darcs 1.0.3 + show_diff = 0 + + if show_diff: + print """ + + """ % { + 'myreponame': config.myreponame, + 'hash': p.hash, + 'file': f + } + else: + print "" % f + + show_diff = 1 + if f in p.adds: + print '' + elif f in p.diradds: + print '' + elif f in p.removes: + print '' + elif f in p.dirremoves: + print '' + elif p.moves.has_key(f): + print '' + show_diff = 0 + else: + print '' + + if show_diff: + print """ + + """ % { + 'myreponame': config.myreponame, + 'hash': p.hash, + 'file': f + } + print '' + print '
+ + %(file)s +%s', + print '[added]', + print '', + print '[added dir]', + print '', + print '[removed]', + print '', + print '[removed dir]', + print '', + print '[moved to "%s"]' % p.moves[f] + print '', + if p.modifies[f].has_key('b'): + # binary modification + print '(binary)' + else: + print '+%(+)d -%(-)d' % p.modifies[f], + print ' + diff +
' + print_footer() + + +def do_tree(dname): + print_header() + print_navbar() + + # the head + print """ +
Current tree
+
+ """ % config.myreponame + + # and the linked, with links + parts = dname.split('/') + print '/ ' + sofar = '/' + for p in parts: + if not p: continue + sofar += '/' + p + print '%s /' % \ + (config.myreponame, sofar, p) + + print """ +
+
+ + """ + + realpath = config.repodir + '/_darcs/current/' + dname + '/' + alt = False + files = os.listdir(realpath) + files.sort() + + # list directories first + dlist = [] + flist = [] + for f in files: + realfile = realpath + f + if os.path.isdir(realfile): + dlist.append(f) + else: + flist.append(f) + files = dlist + flist + + for f in files: + if alt: + print '' + else: + print '' + alt = not alt + realfile = realpath + f + print '' + + if f in dlist: + print """ + + + """ % { + 'myrname': config.myreponame, + 'f': f, + 'newf': filter_file(dname + '/' + f), + } + else: + print """ + + + """ % { + 'myrname': config.myreponame, + 'f': f, + 'fullf': filter_file(dname + '/' + f), + } + print '' + print '
', fperms(realfile), + print '%(f)stree%(f)sheadblob
' + print_footer() + + +def do_headblob(fname): + print_header() + print_navbar(f = fname) + filepath = os.path.dirname(fname) + print """ +
+ """ + + # and the linked, with links + parts = filepath.split('/') + print '/ ' + sofar = '/' + for p in parts: + if not p: continue + sofar += '/' + p + print '%s /' % \ + (config.myreponame, sofar, p) + + print '
' + + # TODO: when we have annotate, put some links around here. + print_blob(fname) + print_footer() + + +def do_plainblob(fname): + print_plain_header() + f = open(config.repodir + '/_darcs/current/' + fname, 'r') + for l in f: + sys.stdout.write(l) + + +def do_shortlog(topi): + print_header() + print_navbar() + print_shortlog(topi = topi) + print_footer() + + +def do_log(topi): + print_header() + print_navbar() + print_log(topi = topi) + print_footer() + + +def do_rss(): + print "Content-type: text/xml; charset=utf-8\n" + print '' + print """ + + + %(reponame)s + %(url)s + %(desc)s + en + """ % { + 'reponame': config.reponame, + 'url': config.myurl + '/' + config.myreponame, + 'desc': config.repodesc, + } + + ps = get_last_patches(20) + for p in ps: + title = time.strftime('%d %b %H:%S', time.localtime(p.date)) + title += ' - ' + p.name + pdate = time.strftime("%a, %d %b %Y %H:%M:%S +0000", + time.localtime(p.date)) + link = '%s/%s;a=commit;h=%s' % (config.myurl, + config.myreponame, p.hash) + print """ + + %(title)s + %(pdate)s + %(link)s + %(desc)s + """ % { + 'title': escape(title), + 'pdate': pdate, + 'link': link, + 'desc': escape(p.name), + } + print ' ' + if p.comment: + print '
' + print escape(p.comment).replace('\n', '
\n') + print ']]>' + print '
' + + print '
' + + +def do_die(): + print_header() + print "

Error! Malformed query

" + print_footer() + sys.exit(1) + + +def do_listrepos(): + import config as all_configs + + # the header here is special since we don't have a repo + print "Content-type: text/html; charset=utf-8\n" + print """ + + + + + + +darcs - Repositories + + + + + + + +
+This is the repository index for a darcsweb site.
+These are all the available repositories.
+
+ + + + + + + """ % { + 'myname': all_configs.base.myname, + 'css': all_configs.base.cssfile, + 'fav': all_configs.base.darcsfav, + 'logo': all_configs.base.darcslogo + } + + # some python magic + alt = False + for conf in dir(all_configs): + if conf.startswith('__'): + continue + c = all_configs.__getattribute__(conf) + if 'reponame' not in dir(c): + continue + name = escape(c.reponame) + desc = escape(c.repodesc) + + if alt: print '' + else: print '' + alt = not alt + print """ + + + + + """ % { + 'myname': all_configs.base.myname, + 'name': name, + 'desc': shorten_str(desc, 60) + } + print "
ProjectDescription
%(name)s%(desc)s
" + print_footer(put_rss = 0) + +def fill_config(name): + import config as all_configs + for conf in dir(all_configs): + if conf.startswith('__'): + continue + c = all_configs.__getattribute__(conf) + if 'reponame' not in dir(c): + continue + if c.reponame == name: + break + else: + # not found + raise + + # fill the configuration + base = all_configs.base + config.myname = base.myname + config.myreponame = base.myname + '?r=' + name + config.myurl = base.myurl + config.darcslogo = base.darcslogo + config.darcsfav = base.darcsfav + config.cssfile = base.cssfile + config.reponame = c.reponame + config.repodesc = c.repodesc + config.repodir = c.repodir + config.repourl = c.repourl + config.repoencoding = c.repoencoding + + +# +# main +# + +form = cgi.FieldStorage() + +# if they don't specify a repo, print the list and exit +if not form.has_key('r'): + do_listrepos() + sys.exit(0) + +# get the repo configuration and fill the config class +current_repo = form['r'].value +fill_config(current_repo) + + +# get the action, or default to summary +if not form.has_key("a"): + action = "summary" +else: + action = filter_an(form["a"].value) + + +# see what should we do according to the received action +if action == "summary": + do_summary() +elif action == "commit": + phash = filter_hash(form["h"].value) + do_commit(phash) +elif action == "commitdiff": + phash = filter_hash(form["h"].value) + do_commitdiff(phash) +elif action == 'headdiff': + phash = filter_hash(form["h"].value) + do_headdiff(phash) +elif action == "filediff": + phash = filter_hash(form["h"].value) + fname = filter_file(form["f"].value) + do_filediff(phash, fname) +elif action == 'headfilediff': + phash = filter_hash(form["h"].value) + fname = filter_file(form["f"].value) + do_file_headdiff(phash, fname) +elif action == "plainfilediff": + phash = filter_hash(form["h"].value) + fname = filter_file(form["f"].value) + do_plainfilediff(phash, fname) +elif action == "shortlog": + if form.has_key("topi"): + topi = int(filter_num(form["topi"].value)) + else: + topi = 0 + do_shortlog(topi) +elif action == "log": + if form.has_key("topi"): + topi = int(filter_num(form["topi"].value)) + else: + topi = 0 + do_log(topi) +elif action == 'headblob': + fname = filter_file(form["f"].value) + do_headblob(fname) +elif action == 'plainblob': + fname = filter_file(form["f"].value) + do_plainblob(fname) +elif action == 'tree': + if form.has_key('f'): + fname = filter_file(form["f"].value) + else: + fname = '/' + do_tree(fname) +elif action == 'rss': + do_rss() +else: + action = "invalid query" + do_die() + + Binary files old-darcsweb/minidarcs.png and new-darcsweb/minidarcs.png differ diff -rN -u old-darcsweb/style.css new-darcsweb/style.css --- old-darcsweb/style.css 1970-01-01 00:00:00.000000000 +0000 +++ new-darcsweb/style.css 2015-04-18 08:11:00.000000000 +0000 @@ -0,0 +1,212 @@ + +/* darcsweb CSS + * Alberto Bertogli (albertogli@telpin.com.ar) + * + * Copied almost entirely from gitweb's, written by Kay Sievers + * and Christian Gierke . + */ + + +body { + font-family: sans-serif; + font-size: 12px; + margin:0px; + border:solid #d9d8d1; + border-width:1px; + margin:10px; +} + +a { + color:#0000cc; +} + +a:hover, a:visited, a:active { + color:#880000; +} + +div.page_header { + height:25px; + padding:8px; + font-size:18px; + font-weight:bold; + background-color:#d9d8d1; +} + +div.page_header a:visited { + color:#0000cc; +} + +div.page_header a:hover { + color:#880000; +} + +div.page_nav { + padding:8px; +} +div.page_nav a:visited { + color:#0000cc; +} + +div.page_path { + padding:8px; + border:solid #d9d8d1; + border-width:0px 0px 1px +} + +div.page_footer { + height:17px; + padding:4px 8px; + background-color: #d9d8d1; +} + +div.page_footer_text { + float:left; + color:#555555; + font-style:italic; +} + +div.page_body { + padding:8px; +} + +div.title, a.title { + display:block; padding:6px 8px; + font-weight:bold; + background-color:#edece6; + text-decoration:none; + color:#000000; +} + +a.title:hover { + background-color: #d9d8d1; +} + +div.title_text { + padding:6px 0px; + border: solid #d9d8d1; + border-width:0px 0px 1px; +} + +div.log_body { + padding:8px 8px 8px 150px; +} + +span.age { + position:relative; + float:left; + width:142px; + font-style:italic; +} + +div.log_link { + padding:0px 8px; + font-size:10px; + font-family:sans-serif; + font-style:normal; + position:relative; + float:left; + width:136px; +} + +div.list_head { + padding:6px 8px 4px; + border:solid #d9d8d1; + border-width:1px 0px 0px; + font-style:italic; +} + +a.list { + text-decoration:none; + color:#000000; +} + +a.list:hover { + text-decoration:underline; + color:#880000; +} + +table { + padding:8px 4px; +} + +th { + padding:2px 5px; + font-size:12px; + text-align:left; +} + +tr.light:hover { + background-color:#edece6; +} + +tr.dark { + background-color:#f6f6f0; +} + +tr.dark:hover { + background-color:#edece6; +} + +td { + padding:2px 5px; + font-size:12px; + vertical-align:top; +} + +td.link { + padding:2px 5px; + font-family:sans-serif; + font-size:10px; +} + +div.pre { + font-family:monospace; + font-size:12px; + white-space:pre; +} + +div.diff_info { + font-family:monospace; + color:#000099; + background-color:#edece6; + font-style:italic; +} + +div.index_include { + border:solid #d9d8d1; + border-width:0px 0px 1px; + padding:12px 8px; +} + +div.search { + margin:4px 8px; + position:absolute; + top:56px; + right:12px +} + +a.linenr { + color:#999999; + text-decoration:none +} + +a.rss_logo { + float:right; + padding:3px 0px; + width:35px; + line-height:10px; + border:1px solid; + border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e; + color:#ffffff; + background-color:#ff6600; + font-weight:bold; + font-family:sans-serif; + font-size:10px; + text-align:center; + text-decoration:none; +} + +a.rss_logo:hover { + background-color:#ee5500; +} +