Sun Nov 13 11:23:24 UTC 2011 pinterface <pix@kepibu.org>
* Add support for calling an external program to generate README markup
Sun Nov 13 11:07:41 UTC 2011 pinterface <pix@kepibu.org>
* minidom chokes on form feed character
Sat Nov 5 10:18:50 UTC 2011 pinterface <pix@kepibu.org>
* Show README in summary view, if it exists
Blatantly stealing the idea from Github, but not as fully implemented.
Tue Nov 1 10:19:57 UTC 2011 pinterface <pix@kepibu.org>
* Add Owner and Last Change columns to repo listing
This is a little ugly due to the global config object. Boo.
Tue Nov 1 09:54:21 UTC 2011 pinterface <pix@kepibu.org>
* Skip comments and blank lines in the author file
Tue Nov 1 08:30:12 UTC 2011 pinterface <pix@kepibu.org>
* Add ability to specify a mailing list URL for repositories
Tue Nov 1 08:12:39 UTC 2011 pinterface <pix@kepibu.org>
* Don't require myurl be specified when cachedir is in use
Wed Nov 24 15:52:33 UTC 2010 Alberto Bertogli <albertito@blitiri.com.ar>
tagged 1.2-rc1
Fri Oct 15 12:55:46 UTC 2010 Dave Love <fx@gnu.org>
* Use diff --quiet.
Prevents `Copying pristine' messages in output with darcs pre-2.5.
Fri Jun 25 00:32:55 UTC 2010 Alberto Bertogli <albertito@blitiri.com.ar>
* Do not use string exceptions
String exceptions are deprecated and will soon no longer be supported at all.
Most of the instances of string exceptions in our code were not meant to be
catched, but just to get meaningful backtraces.
This patch replaces them with appropriate, usually generic, exceptions that
serve the same purpose.
Sun Mar 28 18:21:44 UTC 2010 Simon Michael <simon@joyful.com>
* log view: display the patch name just once, tighten up whitespace
Sun Mar 28 16:40:18 UTC 2010 Simon Michael <simon@joyful.com>
* allow custom "last" value in url, eg to view all patches on one page
Sun Mar 28 16:37:55 UTC 2010 Simon Michael <simon@joyful.com>
* hide darcs' Ignore-this: metadata in log view
Sun Mar 28 18:57:50 UTC 2010 Simon Michael <simon@joyful.com>
* more robust timestamp parsing
With this, darcsweb can list the full darcs repo log.
Thu Mar 26 19:58:42 UTC 2009 gaetan.lehmann@jouy.inra.fr
* add the year in the date field if the patch wasn't made in the current year
Tue Oct 28 15:44:36 UTC 2008 Alberto Bertogli <albertito@blitiri.com.ar>
tagged 1.1
Tue Oct 28 15:43:41 UTC 2008 Alberto Bertogli <albertito@gmail.com>
* Update my email address
Tue Aug 12 09:50:07 UTC 2008 Alexandre Rossi <alexandre.rossi@gmail.com>
* fix pygments disaligned linenos and code when using windows fonts (pygments>0.7 only)
Tue Oct 14 21:18:42 UTC 2008 Alberto Bertogli <albertito@gmail.com>
tagged 1.1-rc1
Tue Oct 14 21:18:29 UTC 2008 Alberto Bertogli <albertito@gmail.com>
* Update HTML version number
Sun Oct 5 15:53:16 UTC 2008 Alberto Bertogli <albertito@gmail.com>
* Import pygments only when needed
Otherwise, we pay for the pygments import (which is noticeable) on every
darcsweb page view.
This follows the lazy module loading that is already being done for other
costly modules (i.e. 're').
Sun Oct 5 15:48:31 UTC 2008 Alberto Bertogli <albertito@gmail.com>
* Ignore broken multidir directories
When a multidir directory is not really a directory or a valid symlink to one,
we want to skip it instead of exploding.
This patch fixes that by doing a simple sanity check while processing the
configured multidirs.
Thanks to Ralph Giles <giles@ghostscript.com> for the report and the patch.
Thu Aug 7 05:31:41 UTC 2008 Alexandre Rossi <alexandre.rossi@gmail.com>
* optional source headblob syntax highlighting using python-pygments
Thu Aug 7 04:41:45 UTC 2008 Alberto Bertogli <albertito@gmail.com>
* Handle old date formats
Very old darcs commits use a different time format, that leaks out in some
operations like annotate. This patch makes darcsweb able to handle it.
Thanks to Simon Michael <simon@joyful.com> for the report and the help.
Sun Aug 3 15:04:07 UTC 2008 Alberto Bertogli <albertito@gmail.com>
* Fix typo in annotate output
Sun Aug 3 15:03:41 UTC 2008 Alberto Bertogli <albertito@gmail.com>
* Simplify annotate code
A very simple change, makes the code more straightforward.
Sun Aug 3 15:02:56 UTC 2008 Alberto Bertogli <albertito@gmail.com>
* Fix annotate so it works with darcs 2
darcs 2 doesn't like files beginning with / as parameters for annotate. Since
darcs 1 doesn't care, remove the '/' before running darcs.
Sun Aug 3 15:00:34 UTC 2008 Alberto Bertogli <albertito@gmail.com>
* Use _darcs/patches instead of _darcs/inventory
In darcs 2, there is no _darcs/inventory, so use the patches directory to find
out the last modification time of the repository.
Thanks to Patrick Waugh (ptwaugh@gmail.com) for providing (and testing) this
fix.
Tue Apr 8 00:11:05 UTC 2008 Alberto Bertogli <albertito@gmail.com>
tagged 1.0
Tue Apr 8 00:10:21 UTC 2008 Alberto Bertogli <albertito@gmail.com>
* Version 1.0
Thu Mar 27 01:06:42 UTC 2008 Alberto Bertogli <albertito@gmail.com>
tagged 1.0-rc2
Thu Mar 27 01:02:58 UTC 2008 Alberto Bertogli <albertito@gmail.com>
* Add an option to change the display name of the repository
It can be used to avoid clashes in multidir entries.
Thanks to Dan Muller for the idea and alternative implementation.
Mon Mar 24 17:31:17 UTC 2008 Miklos Vajna <vmiklos@frugalware.org>
* add support for the DARCSWEB_CONFPATH env var
- useful when hosting multiple darcsweb sites via vhost
Sun Oct 14 15:48:26 UTC 2007 Alberto Bertogli <albertito@gmail.com>
tagged 1.0-rc1
Sun Oct 14 15:42:34 UTC 2007 Alberto Bertogli <albertito@gmail.com>
* Add /etc/darcsweb after '.' in sys.path.
Add it second place, so it goes after '.' but before the normal path. This
allows per-directory config files (desirable for multiple darcsweb
installations on the same machin), and avoids name clashing if there's a
config.py in the standard path.
This was reported by Philipp Kern <pkern@debian.org> in Debian bug 399751
(http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=399751).
Fri Jun 22 09:30:21 UTC 2007 Peter Colberg <peterco@gmx.net>
* Adjust URI scheme according to CGI environment variable HTTPS.
This patch fixes RSS feed links when browsing darcsweb via SSL.
Wed May 16 09:48:24 UTC 2007 Jonathan Buchanan <jonathan.buchanan@gmail.com>
* Fixed bad links in RSS feed when darcsweb URL is automatically detected
When the darcsweb URL is automatically detected, the script name is included in the URL - this results in bad links in RSS feeds like /darcsweb.cgi/darcsweb.cgi?r=projname.
Wed May 16 09:46:25 UTC 2007 Jonathan Buchanan <jonathan.buchanan@gmail.com>
* Fixed a path problem when running on Windows
Repositories in subdirectories have backslashes in their path when running on Windows, which results in bad URLS - replaced these with forward slashes.
diff -rN -u old-darcsweb/README new-darcsweb/README
--- old-darcsweb/README 2013-08-24 05:09:22.000000000 +0000
+++ new-darcsweb/README 2013-08-24 05:09:22.000000000 +0000
@@ -1,7 +1,7 @@
darcsweb - A web interface for darcs
-Alberto Bertogli (albertito@gmail.com)
-----------------------------------------
+Alberto Bertogli (albertito@blitiri.com.ar)
+---------------------------------------------
This is a very simple web interface for darcs, inspired in gitweb (written by
Kay Sievers and Christian Gierke).
diff -rN -u old-darcsweb/config.py.sample new-darcsweb/config.py.sample
--- old-darcsweb/config.py.sample 2013-08-24 05:09:22.000000000 +0000
+++ new-darcsweb/config.py.sample 2013-08-24 05:09:22.000000000 +0000
@@ -84,6 +84,10 @@
# option.
#disable_annotate = True
+ # If you'd like a wider range of README file types than darcsweb
+ # provides, set readme_converter to a program which takes a single
+ # argument--the name of the readme file--and outputs HTML.
+ #readme_converter = 'ruby -rubygems /var/lib/gems/1.8/gems/github-markup-0.5.3/bin/github-markup'
#
@@ -144,7 +148,8 @@
# create a configuration entry for each one, you can use a "multidir" entry,
# which serves as a "template" for all the repositories in that directory.
# The name is taken from the directory, and inside the variables the string
-# "%(name)s" gets expanded to the it.
+# "%(name)s" gets expanded to the it. If displayname is set, "%(dname)s" gets
+# expanded to it; otherwise it's the same as "%(name)s".
#
# If you set multidir_deep to True (note the capitalization) then all
# subdirectories are searched for darcs repositories. Subdirectories starting
@@ -160,6 +165,11 @@
repourl = 'http://example.com/repos/%(name)s/'
repoencoding = 'latin1'
+ # if you want to change the display name of the repositories (i.e. the
+ # name it will have on the listings, urls, etc.), you can set it here.
+ # You can use "%(name)s" expansion, see above.
+ #displayname = "local/%(name)s"
+
# optional, see above
#repoprojurl = 'http://example.com/projects/%(name)s/'
diff -rN -u old-darcsweb/darcsweb.cgi new-darcsweb/darcsweb.cgi
--- old-darcsweb/darcsweb.cgi 2013-08-24 05:09:22.000000000 +0000
+++ new-darcsweb/darcsweb.cgi 2013-08-24 05:09:22.000000000 +0000
@@ -2,7 +2,7 @@
"""
darcsweb - A web interface for darcs
-Alberto Bertogli (albertito@gmail.com)
+Alberto Bertogli (albertito@blitiri.com.ar)
Inspired on gitweb (as of 28/Jun/2005), which is written by Kay Sievers
<kay.sievers@vrfy.org> and Christian Gierke <ch@gierke.de>
@@ -23,10 +23,22 @@
iso_datetime = '%Y-%m-%dT%H:%M:%SZ'
+PATCHES_PER_PAGE = 50
+
# In order to be able to store the config file in /etc/darcsweb, it has to be
# added to sys.path. It's mainly used by distributions, which place the
-# default configuration there.
-sys.path.append('/etc/darcsweb')
+# default configuration there. Add it second place, so it goes after '.' but
+# before the normal path. This allows per-directory config files (desirable
+# for multiple darcsweb installations on the same machin), and avoids name
+# clashing if there's a config.py in the standard path.
+sys.path.insert(1, '/etc/darcsweb')
+
+# Similarly, when hosting multiple darcsweb instrances on the same
+# server, you can just 'SetEnv DARCSWEB_CONFPATH' in the httpd config,
+# and this will have a bigger priority than the system-wide
+# configuration file.
+if 'DARCSWEB_CONFPATH' in os.environ:
+ sys.path.insert(1, os.environ['DARCSWEB_CONFPATH'])
# empty configuration class, we will fill it in later depending on the repo
class config:
@@ -67,7 +79,7 @@
def filter_file(s):
if '..' in s or '"' in s:
- raise 'FilterFile FAILED'
+ raise Exception, 'FilterFile FAILED'
if s == '/':
return s
@@ -104,7 +116,7 @@
return s.decode(e).encode('utf8', 'replace')
except UnicodeDecodeError:
pass
- raise 'DecodingError', config.repoencoding
+ raise UnicodeDecodeError, config.repoencoding
def escape(s):
@@ -118,7 +130,11 @@
# the cached entries will have old data; so in this case just
# return a nice string
t = time.localtime(epoch)
- s = time.strftime("%d %b %H:%M", t)
+ currentYear = time.localtime()[0]
+ if t[0] == currentYear:
+ s = time.strftime("%d %b %H:%M", t)
+ else:
+ s = time.strftime("%d %b %Y %H:%M", t)
return s
age = int(time.time()) - int(epoch)
if age > 60*60*24*365*2:
@@ -176,6 +192,10 @@
return s
+def strip_ignore_this(s):
+ """Strip out darcs' Ignore-this: metadata if present."""
+ import re
+ return re.sub(r'^Ignore-this:[^\n]*\n?','',s)
def highlight(s, l):
"Highlights appearences of s in l"
@@ -282,6 +302,22 @@
lf.close()
+def parse_darcs_time(s):
+ "Try to convert a darcs' time string into a Python time tuple."
+ try:
+ return time.strptime(s, "%Y%m%d%H%M%S")
+ except ValueError:
+ # very old darcs commits use a different format, for example:
+ # "Wed May 21 19:39:10 CEST 2003" or even:
+ # "Sun Sep 21 07:23:57 Pacific Daylight Time 2003"
+ # we can't parse the time zone part reliably, so we ignore it
+ fmt = "%a %b %d %H:%M:%S %Y"
+ parts = s.split()
+ ns = ' '.join(parts[0:4]) + ' ' + parts[-1]
+ return time.strptime(ns, fmt)
+
+
+
#
# generic html functions
#
@@ -293,8 +329,8 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" lang="en-US">
-<!-- darcsweb 0.15
- Alberto Bertogli (albertito@gmail.com).
+<!-- darcsweb 1.1
+ Alberto Bertogli (albertito@blitiri.com.ar).
Based on gitweb, which is written by Kay Sievers <kay.sievers@vrfy.org>
and Christian Gierke <ch@gierke.de>
@@ -533,7 +569,7 @@
sys.stdout = self
return 0
- inv = config.repodir + '/_darcs/inventory'
+ inv = config.repodir + '/_darcs/patches'
cache_lastmod = os.stat(fname).st_mtime
repo_lastmod = os.stat(inv).st_mtime
dw_lastmod = os.stat(sys.argv[0]).st_mtime
@@ -595,10 +631,12 @@
def repo_get_owner():
try:
fd = open(config.repodir + '/_darcs/prefs/author')
- author = fd.readlines()[0].strip()
+ for line in fd:
+ line = line.strip()
+ if line != "" and line[0] != "#":
+ return line.strip()
except:
- author = None
- return author
+ return None
def run_darcs(params):
"""Runs darcs on the repodir with the given params, return a file
@@ -646,7 +684,7 @@
def getdiff(self):
"""Returns a list of lines from the diff -u corresponding with
the patch."""
- params = 'diff -u --match "hash %s"' % self.hash
+ params = 'diff --quiet -u --match "hash %s"' % self.hash
f = run_darcs(params)
return f.readlines()
@@ -723,8 +761,7 @@
au = au[:au.find('<')].strip()
p.shortauthor = fixu8(escape(au))
- td = time.strptime(attrs.get('date', None),
- "%Y%m%d%H%M%S")
+ td = parse_darcs_time(attrs.get('date', None))
p.date = time.mktime(td)
p.date_str = time.strftime("%a, %d %b %Y %H:%M:%S", td)
@@ -812,7 +849,7 @@
if p.inverted:
p.name = 'UNDO: ' + p.name
elif name == 'comment':
- self.db[self.current].comment = fixu8(self.cur_val)
+ self.db[self.current].comment = fixu8(strip_ignore_this(self.cur_val))
elif name == 'add_file':
scv = fixu8(self.cur_val.strip())
self.db[self.current].adds.append(scv)
@@ -893,16 +930,16 @@
return patch
def get_diff(hash):
- return run_darcs('diff -u --match "hash %s"' % hash)
+ return run_darcs('diff --quiet -u --match "hash %s"' % hash)
def get_file_diff(hash, fname):
- return run_darcs('diff -u --match "hash %s" "%s"' % (hash, fname))
+ return run_darcs('diff --quiet -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))
+ return run_darcs('diff --quiet -u --from-match "hash %s" "%s"' % (hash, fname))
def get_patch_headdiff(hash):
- return run_darcs('diff -u --from-match "hash %s"' % hash)
+ return run_darcs('diff --quiet -u --from-match "hash %s"' % hash)
def get_raw_diff(hash):
import gzip
@@ -962,7 +999,7 @@
# minidom know the source encoding
s = ""
for i in src:
- s += fixu8(i)
+ s += fixu8(i).replace('', '^L')
dom = xml.dom.minidom.parseString(s)
@@ -984,8 +1021,7 @@
lastname = lastname.childNodes[0].wholeText
annotate.lastchange_name = fixu8(lastname)
- lastdate = lastpatch.getAttribute("date")
- lastdate = time.strptime(lastdate, "%Y%m%d%H%M%S")
+ lastdate = parse_darcs_time(lastpatch.getAttribute("date"))
annotate.lastchange_date = lastdate
annotate.patches[annotate.lastchange_hash] = annotate.lastchange_date
@@ -1007,7 +1043,7 @@
phash = patch.getAttribute("hash")
pauthor = patch.getAttribute("author")
pdate = patch.getAttribute("date")
- pdate = time.strptime(pdate, "%Y%m%d%H%M%S")
+ pdate = parse_darcs_time(pdate)
else:
# added lines inherit the creation from the annotate
# patch
@@ -1040,16 +1076,44 @@
return annotate
def get_annotate(fname, hash = None):
+ if config.disable_annotate:
+ return None
+
cmd = 'annotate --xml-output'
if hash:
cmd += ' --match="hash %s"' % hash
+
+ if fname.startswith('/'):
+ # darcs 2 doesn't like files starting with /, and darcs 1
+ # doesn't really care
+ fname = fname[1:]
cmd += ' "%s"' % fname
- if not config.disable_annotate:
- out = run_darcs(cmd)
- else:
- return None
- return parse_annotate(out)
+ return parse_annotate(run_darcs(cmd))
+
+def get_readme():
+ import glob
+ readmes = glob.glob("README*")
+ if len(readmes) == 0: return False, False
+ import re
+ for p in readmes:
+ file = os.path.basename(p)
+ if re.search('\.(md|markdown)$', p):
+ import codecs
+ import markdown
+ f = codecs.open(p, encoding=config.repoencoding[0])
+ str = f.read()
+ html = markdown.markdown(str, ['extra', 'codehilite(css_class=page_body)'])
+ return file, fixu8(html)
+ elif re.search('README$', p):
+ f = open(p)
+ str = f.read()
+ return file, '<pre>%s</pre>' % fixu8(escape(str))
+ # We can't handle this ourselves, try shelling out
+ if not config.readme_converter: return False, False
+ cmd = '%s "%s"' % (config.readme_converter, readmes[0])
+ inf, outf = os.popen2(cmd, 't')
+ return os.path.basename(readmes[0]), fixu8(outf.read())
#
@@ -1107,7 +1171,7 @@
print '<div %s>' % cl + escape(l) + '</div>'
-def print_shortlog(last = 50, topi = 0, fname = None):
+def print_shortlog(last = PATCHES_PER_PAGE, topi = 0, fname = None):
ps = get_last_patches(last, topi, fname)
if fname:
@@ -1181,7 +1245,14 @@
print "</table>"
-def print_log(last = 50, topi = 0):
+def print_readme():
+ head, body = get_readme()
+ if not head: return False
+ print '<div class="title">%s</div>' % head
+ print '<section>%s</section' % body
+
+
+def print_log(last = PATCHES_PER_PAGE, topi = 0):
ps = get_last_patches(last, topi)
if topi != 0:
@@ -1211,8 +1282,6 @@
<i>%(author)s [%(date)s]</i><br/>
</div>
<div class="log_body">
- %(desc)s<br/>
- <br/>
%(comment)s
</div>
@@ -1234,14 +1303,37 @@
def print_blob(fname):
print '<div class="page_path"><b>%s</b></div>' % escape(fname)
- print '<div class="page_body">'
if isbinary(fname):
print """
-<i>This is a binary file and it's contents will not be displayed.</i>
+<div class="page_body">
+<i>This is a binary file and its contents will not be displayed.</i>
</div>
"""
return
+ try:
+ import pygments
+ except ImportError:
+ pygments = False
+
+ if not pygments:
+ print_blob_simple(fname)
+ return
+ else:
+ try:
+ print_blob_highlighted(fname)
+ except ValueError:
+ # pygments couldn't guess a lexer to highlight the code, try
+ # another method with sampling the file contents.
+ try:
+ print_blob_highlighted(fname, sample_code=True)
+ except ValueError:
+ # pygments really could not find any lexer for this file.
+ print_blob_simple(fname)
+
+def print_blob_simple(fname):
+ print '<div class="page_body">'
+
f = open(realpath(fname), 'r')
count = 1
for l in f:
@@ -1261,11 +1353,63 @@
count += 1
print '</div>'
+def print_blob_highlighted(fname, sample_code=False):
+ import pygments
+ import pygments.lexers
+ import pygments.formatters
+
+ code = open(realpath(fname), 'r').read()
+ if sample_code:
+ lexer = pygments.lexers.guess_lexer(code[:200],
+ encoding=config.repoencoding[0])
+ else:
+ lexer = pygments.lexers.guess_lexer_for_filename(fname, code[:200],
+ encoding=config.repoencoding[0])
+
+ pygments_version = map(int, pygments.__version__.split('.'))
+ if pygments_version >= [0, 7]:
+ linenos_method = 'inline'
+ else:
+ linenos_method = True
+ formatter = pygments.formatters.HtmlFormatter(linenos=linenos_method,
+ cssclass='page_body')
+
+ print pygments.highlight(code, lexer, formatter)
+ print """<script type='text/javascript'>
+(function () {
+ if (!document.getElementsByClassName || !document.evaluate || !window.addEventListener)
+ return;
+
+ function fakeAnchors () {
+ var pbody = document.getElementsByClassName("page_body")[0];
+ var anchor = window.location.hash;
+ if (!pbody || "" == anchor || "#" == anchor) return;
+
+ /* Avoid xpath injection, because so far as I can tell there's no way to set
+ xpath variables from JavaScript. */
+ if (anchor.match(/['"]/)) return;
+
+ anchor = anchor.substr(1);
+ var parts = anchor.split(/ /);
+ var xpq = '//span[string()="'+parts[0]+'"]';
+ for (var i = 1; i < parts.length; i++) {
+ xpq += '//following-sibling::span[1][string()="'+parts[i]+'"]';
+ }
+ var res = document.evaluate(xpq, pbody, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
+ if (!res || !res.singleNodeValue) return;
+
+ res.singleNodeValue.scrollIntoView(true);
+ };
+ window.addEventListener("DOMContentLoaded", fakeAnchors, false);
+ window.addEventListener("hashchange", fakeAnchors, false);
+})();
+</script>"""
+
def print_annotate(ann, style):
print '<div class="page_body">'
if isbinary(ann.fname):
print """
-<i>This is a binary file and it's contents will not be displayed.</i>
+<i>This is a binary file and its contents will not be displayed.</i>
</div>
"""
return
@@ -1402,9 +1546,14 @@
print ' <tr><td>project url</td>'
print ' <td><a href="%(url)s">%(url)s</a></td></tr>' % \
{ 'url': config.repoprojurl }
+ if config.repolisturl:
+ print ' <tr><td>mailing list url</td>'
+ print ' <td><a href="%(url)s">%(url)s</a></td></tr>' % \
+ { 'url': config.repolisturl }
print '</table>'
print_shortlog(15)
+ print_readme()
print_footer()
@@ -1900,28 +2049,28 @@
sys.stdout.write(l.text)
-def do_shortlog(topi):
+def do_shortlog(topi, last=PATCHES_PER_PAGE):
print_header()
print_navbar()
- print_shortlog(topi = topi)
+ print_shortlog(topi = topi, last = last)
print_footer()
-def do_filehistory(topi, f):
+def do_filehistory(topi, f, last=PATCHES_PER_PAGE):
print_header()
print_navbar(f = fname)
- print_shortlog(topi = topi, fname = fname)
+ print_shortlog(topi = topi, fname = fname, last = last)
print_footer()
-def do_log(topi):
+def do_log(topi, last=PATCHES_PER_PAGE):
print_header()
print_navbar()
- print_log(topi = topi)
+ print_log(topi = topi, last = last)
print_footer()
def do_atom():
print "Content-type: application/atom+xml; charset=utf-8\n"
print '<?xml version="1.0" encoding="utf-8"?>'
- inv = config.repodir + '/_darcs/inventory'
+ inv = config.repodir + '/_darcs/patches'
repo_lastmod = os.stat(inv).st_mtime
str_lastmod = time.strftime(iso_datetime,
time.localtime(repo_lastmod))
@@ -2155,6 +2304,8 @@
<tr>
<th>Project</th>
<th>Description</th>
+<th>Owner</th>
+<th>Last Change</th>
<th></th>
</tr>
""" % {
@@ -2167,7 +2318,9 @@
# some python magic
alt = True
- for conf in dir(all_configs):
+ confs = dir(all_configs)
+ confs.sort(key=str.lower)
+ for conf in confs:
if conf.startswith('__'):
continue
c = all_configs.__getattribute__(conf)
@@ -2179,9 +2332,14 @@
if alt: print '<tr class="dark">'
else: print '<tr class="light">'
alt = not alt
+ try: orig_repodir = config.repodir
+ except: orig_repodir = None
+ config.repodir = c.repodir
print """
<td><a class="list" href="%(myname)s?r=%(name)s;a=summary">%(dname)s</a></td>
<td>%(desc)s</td>
+<td>%(owner)s</td>
+<td>%(lastchange)s</td>
<td class="link"><a href="%(myname)s?r=%(name)s;a=summary">summary</a> |
<a href="%(myname)s?r=%(name)s;a=shortlog">shortlog</a> |
<a href="%(myname)s?r=%(name)s;a=log">log</a> |
@@ -2192,8 +2350,11 @@
'myname': config.myname,
'dname': name,
'name': urllib.quote(name),
- 'desc': shorten_str(desc, 60)
+ 'desc': shorten_str(desc, 60),
+ 'owner': escape(repo_get_owner() or ''),
+ 'lastchange': how_old(os.stat(c.repodir + '/_darcs/patches').st_mtime),
}
+ config.repodir = orig_repodir
print "</table>"
print_footer(put_rss = 0)
@@ -2210,6 +2371,9 @@
if 'multidir' not in dir(c):
continue
+ if not os.path.isdir(c.multidir):
+ continue
+
if 'exclude' not in dir(c):
c.exclude = []
@@ -2227,6 +2391,7 @@
entries.sort()
for name in entries:
+ name = name.replace('\\', '/')
if name.startswith('.'):
continue
fulldir = c.multidir + '/' + name
@@ -2235,6 +2400,15 @@
if name in c.exclude:
continue
+ # set the display name at the beginning, so it can be
+ # used by the other replaces
+ if 'displayname' in dir(c):
+ dname = c.displayname % { 'name': name }
+ else:
+ dname = name
+
+ rep_dict = { 'name': name, 'dname': dname }
+
if 'autoexclude' in dir(c) and c.autoexclude:
dpath = fulldir + \
'/_darcs/third_party/darcsweb'
@@ -2247,9 +2421,9 @@
if os.access(dpath, os.R_OK):
desc = open(dpath).readline().rstrip("\n")
else:
- desc = c.repodesc % { 'name': name }
+ desc = c.repodesc % rep_dict
else:
- desc = c.repodesc % { 'name': name }
+ desc = c.repodesc % rep_dict
if 'autourl' in dir(c) and c.autourl:
dpath = fulldir + \
@@ -2257,9 +2431,9 @@
if os.access(dpath, os.R_OK):
url = open(dpath).readline().rstrip("\n")
else:
- url = c.repourl % { 'name': name }
+ url = c.repourl % rep_dict
else:
- url = c.repourl % { 'name': name }
+ url = c.repourl % rep_dict
if 'autoprojurl' in dir(c) and c.autoprojurl:
dpath = fulldir + \
@@ -2267,27 +2441,43 @@
if os.access(dpath, os.R_OK):
projurl = open(dpath).readline().rstrip("\n")
elif 'repoprojurl' in dir(c):
- projurl = c.repoprojurl % {'name': name}
+ projurl = c.repoprojurl % rep_dict
else:
projurl = None
elif 'repoprojurl' in dir(c):
- projurl = c.repoprojurl % { 'name': name }
+ projurl = c.repoprojurl % rep_dict
else:
projurl = None
+ if 'autolisturl' in dir(c) and c.autolisturl:
+ dpath = fulldir + \
+ '/_darcs/third_party/darcsweb/listurl'
+ if os.access(dpath, os.R_OK):
+ listurl = open(dpath).readline().rstrip("\n")
+ elif 'repolisturl' in dir(c):
+ listurl = c.repolisturl % rep_dict
+ else:
+ listurl = None
+ elif 'repolisturl' in dir(c):
+ listurl = c.repolisturl % rep_dict
+ else:
+ listurl = None
+
rdir = fulldir
class tmp_config:
- reponame = name
+ reponame = dname
repodir = rdir
repodesc = desc
repourl = url
repoencoding = c.repoencoding
repoprojurl = projurl
+ repolisturl = listurl
if 'footer' in dir(c):
footer = c.footer
- config.__setattr__(name, tmp_config)
+ # index by display name to avoid clashes
+ config.__setattr__(dname, tmp_config)
def fill_config(name = None):
import config as all_configs
@@ -2307,7 +2497,7 @@
break
else:
# not found
- raise "RepoNotFound", name
+ raise Exception, "Repo not found: " + repr(name)
# fill the configuration
base = all_configs.base
@@ -2317,15 +2507,16 @@
else:
config.myname = base.myname
- if 'myurl' not in dir(base) and 'cachedir' not in dir(base):
+ if 'myurl' not in dir(base):
n = os.environ['SERVER_NAME']
p = os.environ['SERVER_PORT']
- s = os.environ['SCRIPT_NAME']
- if p == '80':
+ s = os.path.dirname(os.environ['SCRIPT_NAME'])
+ u = os.environ.get('HTTPS', 'off') in ('on', '1')
+ if not u and p == '80' or u and p == '443':
p = ''
else:
p = ':' + p
- config.myurl = 'http://%s%s%s' % (n, p, s)
+ config.myurl = 'http%s://%s%s%s' % (u and 's' or '', n, p, s)
else:
config.myurl = base.myurl
@@ -2343,6 +2534,10 @@
if 'repoprojurl' in dir(c):
config.repoprojurl = c.repoprojurl
+ config.repolisturl = None
+ if 'repolisturl' in dir(c):
+ config.repolisturl = c.repolisturl
+
# repoencoding must be a tuple
if isinstance(c.repoencoding, str):
config.repoencoding = (c.repoencoding, )
@@ -2399,6 +2594,11 @@
else:
config.disable_annotate = False
+ if "readme_converter" in dir(base):
+ config.readme_converter = base.readme_converter
+ else:
+ config.readme_converter = False
+
#
@@ -2434,7 +2634,7 @@
url_request = os.environ['QUERY_STRING']
# create a string representation of the request, ignoring all the
# unused parameters to avoid DoS
- params = ['r', 'a', 'f', 'h', 'topi']
+ params = ['r', 'a', 'f', 'h', 'topi', 'last']
params = [ x for x in form.keys() if x in params ]
url_request = [ (x, form[x].value) for x in params ]
url_request.sort()
@@ -2539,7 +2739,11 @@
topi = int(filter_num(form["topi"].value))
else:
topi = 0
- do_shortlog(topi)
+ if form.has_key("last"):
+ last = int(filter_num(form["last"].value))
+ else:
+ last = PATCHES_PER_PAGE
+ do_shortlog(topi=topi,last=last)
elif action == "filehistory":
if form.has_key("topi"):
@@ -2547,14 +2751,22 @@
else:
topi = 0
fname = filter_file(form["f"].value)
- do_filehistory(topi, fname)
+ if form.has_key("last"):
+ last = int(filter_num(form["last"].value))
+ else:
+ last = PATCHES_PER_PAGE
+ do_filehistory(topi, fname, last=last)
elif action == "log":
if form.has_key("topi"):
topi = int(filter_num(form["topi"].value))
else:
topi = 0
- do_log(topi)
+ if form.has_key("last"):
+ last = int(filter_num(form["last"].value))
+ else:
+ last = PATCHES_PER_PAGE
+ do_log(topi, last=last)
elif action == 'headblob':
fname = filter_file(form["f"].value)
diff -rN -u old-darcsweb/style.css new-darcsweb/style.css
--- old-darcsweb/style.css 2013-08-24 05:09:22.000000000 +0000
+++ new-darcsweb/style.css 2013-08-24 05:09:22.000000000 +0000
@@ -1,6 +1,6 @@
/* darcsweb CSS
- * Alberto Bertogli (albertito@gmail.com)
+ * Alberto Bertogli (albertito@blitiri.com.ar)
*
* Copied almost entirely from gitweb's, written by Kay Sievers
* <kay.sievers@vrfy.org> and Christian Gierke <ch@gierke.de>.
@@ -9,11 +9,12 @@
body {
font-family: sans-serif;
- font-size: 12px;
+ font-size: 1em;
margin:0px;
border:solid #d9d8d1;
border-width:1px;
- margin:10px;
+ margin:0.8em;
+ margin-bottom: 60%;
}
a {
@@ -25,37 +26,29 @@
}
div.page_header {
- height:25px;
- padding:8px;
- font-size:18px;
+ padding:0.5em;
+ font-size:1.5em;
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;
+ padding:0.5em;
}
div.page_path {
- padding:8px;
+ padding:0.5em;
border:solid #d9d8d1;
border-width:0px 0px 1px
}
div.page_footer {
- height:17px;
- padding:4px 8px;
+ overflow:auto;
+ padding:0.25em 0.5em;
background-color: #d9d8d1;
}
@@ -66,7 +59,7 @@
}
div.page_body {
- padding:8px;
+ padding:0.5em;
}
div.search_box {
@@ -75,18 +68,16 @@
}
input.search_text {
- font-size:xx-small;
+ font-size:0.66em;
background-color: #edece6;
- vertical-align: top;
}
input.search_button {
- font-size:xx-small;
- vertical-align: top;
+ font-size:0.66em;
}
div.title, a.title {
- display:block; padding:6px 8px;
+ display:block; padding:0.5em;
font-weight:bold;
background-color:#edece6;
text-decoration:none;
@@ -98,34 +89,35 @@
}
div.title_text {
- padding:6px 0px;
+ padding: 0.5em 0;
border: solid #d9d8d1;
border-width:0px 0px 1px;
}
div.log_body {
- padding:8px 8px 8px 150px;
+ padding:0.5em;
+ padding-left:10.5em;
}
span.age {
position:relative;
float:left;
- width:142px;
+ width:10em;
font-style:italic;
}
div.log_link {
- padding:0px 8px;
- font-size:10px;
+ padding:0 0 0 0.625em;
+ font-size:0.8em;
font-family:sans-serif;
font-style:normal;
position:relative;
float:left;
- width:136px;
+ width:12.5em;
}
div.list_head {
- padding:6px 8px 4px;
+ padding:0.5em;
border:solid #d9d8d1;
border-width:1px 0px 0px;
font-style:italic;
@@ -153,12 +145,11 @@
table {
/*clear:both;*/
- padding:8px 4px;
+ padding:0.5em 0.25em;
}
th {
- padding:2px 5px;
- font-size:12px;
+ padding:0.25em 0.5em;
text-align:left;
}
@@ -183,20 +174,15 @@
}
td {
- padding:2px 5px;
- font-size:12px;
- vertical-align:top;
+ padding:0.25em 0.5em;
}
td.link {
- padding:2px 5px;
- font-family:sans-serif;
- font-size:10px;
+ font-size:0.8em;
}
div.pre {
font-family:monospace;
- font-size:12px;
white-space:pre;
}
@@ -220,7 +206,7 @@
right:12px;
}
-a.linenr {
+a.linenr, .linenos {
color:#999999;
text-decoration:none;
}
@@ -228,7 +214,6 @@
a.annotate_desc {
color:#999999;
text-decoration:none;
- font-size:11px;
}
a.annotate_desc:hover {
@@ -237,16 +222,13 @@
a.rss_logo {
float:right;
- padding:3px 0px;
- width:35px;
- line-height:10px;
- border:1px solid;
- border-color:#fcc7a5 #7d3302 #3e1a01 #ff954e;
+ padding:0.25em 0.5em;
+ border:1px outset;
color:#ffffff;
background-color:#ff6600;
font-weight:bold;
font-family:sans-serif;
- font-size:10px;
+ font-size:0.8em;
text-align:center;
text-decoration:none;
}
@@ -256,9 +238,76 @@
}
img.logo {
- border-width:0px;
+ border-width:0;
vertical-align:top;
- margin-left:12pt;
- margin-right:5pt;
+ margin-left:1em;
+ margin-right:0.25em;
}
+
+/* Syntax highlighting related styles. This is highly dependent on what
+ * python-pygments generates.
+ * This was generated using the following commands in a python interpreter and
+ * slightly modified after.
+ * >>> import pygments
+ * >>> pygments.formatters.HtmlFormatter().get_style_defs('.page_body')
+ */
+.page_body table { margin: 0; padding: 0;}
+.page_body pre { margin : 0; padding: 0; }
+.page_body .c { color: #008800; font-style: italic } /* Comment */
+.page_body .err { border: 1px solid #FF0000 } /* Error */
+.page_body .k { color: #AA22FF; font-weight: bold } /* Keyword */
+.page_body .o { color: #666666 } /* Operator */
+.page_body .cm { color: #008800; font-style: italic } /* Comment.Multiline */
+.page_body .cp { color: #008800 } /* Comment.Preproc */
+.page_body .c1 { color: #008800; font-style: italic } /* Comment.Single */
+.page_body .gd { color: #A00000 } /* Generic.Deleted */
+.page_body .ge { font-style: italic } /* Generic.Emph */
+.page_body .gr { color: #FF0000 } /* Generic.Error */
+.page_body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.page_body .gi { color: #00A000 } /* Generic.Inserted */
+.page_body .go { color: #808080 } /* Generic.Output */
+.page_body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.page_body .gs { font-weight: bold } /* Generic.Strong */
+.page_body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.page_body .gt { color: #0040D0 } /* Generic.Traceback */
+.page_body .kc { color: #AA22FF; font-weight: bold } /* Keyword.Constant */
+.page_body .kd { color: #AA22FF; font-weight: bold } /* Keyword.Declaration */
+.page_body .kp { color: #AA22FF } /* Keyword.Pseudo */
+.page_body .kr { color: #AA22FF; font-weight: bold } /* Keyword.Reserved */
+.page_body .kt { color: #AA22FF; font-weight: bold } /* Keyword.Type */
+.page_body .m { color: #666666 } /* Literal.Number */
+.page_body .s { color: #BB4444 } /* Literal.String */
+.page_body .na { color: #BB4444 } /* Name.Attribute */
+.page_body .nb { color: #AA22FF } /* Name.Builtin */
+.page_body .nc { color: #0000FF } /* Name.Class */
+.page_body .no { color: #880000 } /* Name.Constant */
+.page_body .nd { color: #AA22FF } /* Name.Decorator */
+.page_body .ni { color: #999999; font-weight: bold } /* Name.Entity */
+.page_body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.page_body .nf { color: #00A000 } /* Name.Function */
+.page_body .nl { color: #A0A000 } /* Name.Label */
+.page_body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+.page_body .nt { color: #008000; font-weight: bold } /* Name.Tag */
+.page_body .nv { color: #B8860B } /* Name.Variable */
+.page_body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.page_body .mf { color: #666666 } /* Literal.Number.Float */
+.page_body .mh { color: #666666 } /* Literal.Number.Hex */
+.page_body .mi { color: #666666 } /* Literal.Number.Integer */
+.page_body .mo { color: #666666 } /* Literal.Number.Oct */
+.page_body .sb { color: #BB4444 } /* Literal.String.Backtick */
+.page_body .sc { color: #BB4444 } /* Literal.String.Char */
+.page_body .sd { color: #BB4444; font-style: italic } /* Literal.String.Doc */
+.page_body .s2 { color: #BB4444 } /* Literal.String.Double */
+.page_body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.page_body .sh { color: #BB4444 } /* Literal.String.Heredoc */
+.page_body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.page_body .sx { color: #008000 } /* Literal.String.Other */
+.page_body .sr { color: #BB6688 } /* Literal.String.Regex */
+.page_body .s1 { color: #BB4444 } /* Literal.String.Single */
+.page_body .ss { color: #B8860B } /* Literal.String.Symbol */
+.page_body .bp { color: #AA22FF } /* Name.Builtin.Pseudo */
+.page_body .vc { color: #B8860B } /* Name.Variable.Class */
+.page_body .vg { color: #B8860B } /* Name.Variable.Global */
+.page_body .vi { color: #B8860B } /* Name.Variable.Instance */
+.page_body .il { color: #666666 } /* Literal.Number.Integer.Long */