Sun Nov 13 11:23:24 UTC 2011 pinterface * Add support for calling an external program to generate README markup Sun Nov 13 11:07:41 UTC 2011 pinterface * minidom chokes on form feed character Sat Nov 5 10:18:50 UTC 2011 pinterface * 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 * 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 * Skip comments and blank lines in the author file Tue Nov 1 08:30:12 UTC 2011 pinterface * Add ability to specify a mailing list URL for repositories Tue Nov 1 08:12:39 UTC 2011 pinterface * Don't require myurl be specified when cachedir is in use Wed Nov 24 15:52:33 UTC 2010 Alberto Bertogli tagged 1.2-rc1 Fri Oct 15 12:55:46 UTC 2010 Dave Love * Use diff --quiet. Prevents `Copying pristine' messages in output with darcs pre-2.5. Fri Jun 25 00:32:55 UTC 2010 Alberto Bertogli * 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 * log view: display the patch name just once, tighten up whitespace Sun Mar 28 16:40:18 UTC 2010 Simon Michael * allow custom "last" value in url, eg to view all patches on one page Sun Mar 28 16:37:55 UTC 2010 Simon Michael * hide darcs' Ignore-this: metadata in log view Sun Mar 28 18:57:50 UTC 2010 Simon Michael * 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 tagged 1.1 Tue Oct 28 15:43:41 UTC 2008 Alberto Bertogli * Update my email address Tue Aug 12 09:50:07 UTC 2008 Alexandre Rossi * fix pygments disaligned linenos and code when using windows fonts (pygments>0.7 only) Tue Oct 14 21:18:42 UTC 2008 Alberto Bertogli tagged 1.1-rc1 Tue Oct 14 21:18:29 UTC 2008 Alberto Bertogli * Update HTML version number Sun Oct 5 15:53:16 UTC 2008 Alberto Bertogli * 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 * 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 for the report and the patch. Thu Aug 7 05:31:41 UTC 2008 Alexandre Rossi * optional source headblob syntax highlighting using python-pygments Thu Aug 7 04:41:45 UTC 2008 Alberto Bertogli * 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 for the report and the help. Sun Aug 3 15:04:07 UTC 2008 Alberto Bertogli * Fix typo in annotate output Sun Aug 3 15:03:41 UTC 2008 Alberto Bertogli * Simplify annotate code A very simple change, makes the code more straightforward. Sun Aug 3 15:02:56 UTC 2008 Alberto Bertogli * 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 * 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 tagged 1.0 Tue Apr 8 00:10:21 UTC 2008 Alberto Bertogli * Version 1.0 Thu Mar 27 01:06:42 UTC 2008 Alberto Bertogli tagged 1.0-rc2 Thu Mar 27 01:02:58 UTC 2008 Alberto Bertogli * 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 * 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 tagged 1.0-rc1 Sun Oct 14 15:42:34 UTC 2007 Alberto Bertogli * 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 in Debian bug 399751 (http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=399751). Fri Jun 22 09:30:21 UTC 2007 Peter Colberg * 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 * 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 * 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. Thu May 3 16:18:30 UTC 2007 VMiklos * display the size of the files in tree view Mon Apr 16 19:06:18 UTC 2007 Alberto Bertogli * Soften some colors in commitdiff. Mon Apr 16 09:13:34 UTC 2007 Alexandre Rossi * only read the first line of _darcs/third_party/darcsweb/* files Mon Apr 16 09:11:29 UTC 2007 Alexandre Rossi * make color commitdiff more readable when parts of a file are skipped Fri Mar 16 19:27:14 UTC 2007 VMiklos * new optional config varuable: disable_annotate * the annotate feature is still enabled by default * this can be useful on slow machines Wed Apr 4 15:09:32 UTC 2007 Alexandre Rossi * closing tag in project url link Wed Apr 4 12:15:12 UTC 2007 Alexandre Rossi * typo in sample config autoprojurl description Thu Feb 1 17:30:50 UTC 2007 VMiklos * new optional config varuable: author_links it is disabled by default and an example for cia is included Wed Jan 24 23:38:07 UTC 2007 Alberto Bertogli * Check python version before running darcsweb. This patch adds a check to verify that the python version is >= 2.3, so the user doesn't get strange errors when using older python interpreters. Thanks to Mark Stosberg for the suggestion. Wed Jan 17 23:02:12 UTC 2007 jonathan.buchanan@gmail.com * The DARCS_DONT_ESCAPE_8BIT environment variable is now set using os.environ before running darcs and restored to its original state after running darcs - makes darcsweb work on Windows Mon Dec 25 22:18:20 UTC 2006 Alberto Bertogli * Add support for regexp-based replacing. Add an option to replace text in the summary, using regexps. This allows you to configure darcsweb to automatically detect and put links to other commits or bug numbers. This patch is heavily based on the one written by Kirill Smelkov (kirr@mns.spb.ru). Thu Nov 30 09:37:41 UTC 2006 Kirill Smelkov * string.join is deprecated Mon Dec 25 21:43:36 UTC 2006 Alberto Bertogli * Change my email address. Tue Oct 10 03:04:21 UTC 2006 Alberto Bertogli tagged 0.16 Wed Aug 9 17:54:30 UTC 2006 Alberto Bertogli * Handle empty lines in print_darcs_diff(). A "setpref" patch can output empty lines when seen with darcs_diff. Handle that case by ignoring them. Thanks to VMiklos from reporting it. Mon Jul 31 05:29:33 UTC 2006 Alberto Bertogli tagged 0.16-rc1 Mon Jul 31 05:28:34 UTC 2006 Alberto Bertogli * Remove some unnecessary newlines in html output. Mon Jul 31 05:10:03 UTC 2006 Alberto Bertogli * Make browsing files in tree root easier. This patch makes it easier to browse files in the root of the tree, by making it simpler to return to the root tree view. Mon Jul 31 04:54:05 UTC 2006 Alberto Bertogli * Allow projects to link to external sites. Add a 'repoprojurl' configuration option to put a reference to an external project site. It appears in the project summary, just like url. It also supports multidir, both automatic detection in the third_party directory, and static construction. Thanks to Alexandre Rossi for the idea and preliminary patch. Mon Jul 31 04:24:50 UTC 2006 Alberto Bertogli * Convert remaining auriga references to example.com. Mon Jul 31 04:10:20 UTC 2006 Alberto Bertogli * Implement url and scriptname autodetection. This patch implements autodetection of the url and scriptname of darcsweb. It's based on a suggestion from Tobias Gruetzmacher. It relies on variables according to the CGI/1.1 standard, and works only if cache is not enabled. If it is, the variables must be set manually as before. The problem with the cache is multi-host access: if a page not in the cache is accesed as http://hosta/dw/..., then the cached page will reference "hosta". If then it's accessed from hostb, there will be a reference to hosta that it's possibly broken. It's actually a quite common case when you have localhost and external access. Fri Jul 14 19:27:30 UTC 2006 Alberto Bertogli * Add autourl support for multidir. This patch adds a new option for multidir config entries, named "autourl". If set, it will take the contents of "_darcs/third_party/darcsweb/url" and use it as repourl. Thanks to Marco Baringer for suggestion and preliminary patch. Fri Jul 14 18:09:52 UTC 2006 Alberto Bertogli * Use example.com for url examples. Thu Jul 6 17:04:59 UTC 2006 Alberto Bertogli * Put directories in blue on tree view. To make directories easy to spot, make them blue and with a "/" at the end. Then they become obvious, but not too distracting. Thanks to Dan Pescu for all the suggestions. Mon May 29 15:04:37 UTC 2006 Alberto Bertogli * Escape repository descriptions. Escape all repository descriptions, because they might contain unfrendly characters for XML (including HTML, ATOM and RSS). Thanks to Clive Crous for the report and a slightly different patch. Sat May 13 23:23:21 UTC 2006 Alberto Bertogli * Support the new pristine directory. darcs 1.0.7 uses a new name for the pristine directory, "pristine", instead of the classic "current". This patch adds support for that directory, without dropping "current" because it's still heavily used in existing repositories. Wed Mar 8 02:13:19 UTC 2006 Alberto Bertogli tagged 0.15 Wed Mar 8 02:13:01 UTC 2006 Alberto Bertogli * Version 0.15. Fri Feb 24 01:43:41 UTC 2006 Alberto Bertogli tagged 0.15-rc1 Fri Feb 24 01:37:00 UTC 2006 Alberto Bertogli * Fix repoencoding, this time for real (I hope). Fri Feb 24 00:54:19 UTC 2006 Alberto Bertogli * Fix to allow having a single repoencoding. Fri Feb 24 00:31:28 UTC 2006 Alberto Bertogli * Rewrite encoding handling. This patch rewrites fixu8() and affects a couple of places where decode() was used directly. It has the benefit of removing the ugly previous function, replacing it with... well, another ugly function, but with a different kind of uglyness. It also supports multiple encodings, so if the first one fails, a second one is tried (and so on). There still are some corner cases with file named in alternative encodings, but should work much better than the older code. Thu Feb 23 19:47:31 UTC 2006 Alberto Bertogli * Include the repository in log_times() output. Thu Feb 23 18:51:21 UTC 2006 Alberto Bertogli * Quote filenames in several places. There were several places using filenames in URLs without quoting them properly, this fixes them. Thanks to VMiklos for the report. Thu Feb 23 18:34:51 UTC 2006 Alberto Bertogli * Filter '"' in filenames. For security, don't allow '"' in filenames. If there is high demand, some alternative workaround could be implemented. Thu Feb 23 17:21:30 UTC 2006 Alberto Bertogli * Add /etc/darcsweb to the module lookup path. This allows darcsweb's configuration to be in /etc/darcsweb instead of the current directory. It's mostly wanted by distributions which want to isolate the config file to keep things clean. A similar patch was sent by Gaetan Lehmann (Mandriva), VMiklos (Frugalware) and Fabian Linzberger (Debian). Wed Feb 22 04:47:15 UTC 2006 Alberto Bertogli * Move some one-time used imports to the user location. As a minor but noticeable optimization to darcsweb's load time, move the importing of sha and email.Utils modules to the place where they're used (only one in both cases). This takes the time spent on imports from 0.027s to 0.021s, which will probably be slightly noticeable on slower machines, reducing the time taken by a cache hit from 0.030s to 0.024s (on misses it doesn't make a difference). Tue Feb 21 21:24:30 UTC 2006 Alberto Bertogli * Begin with alt = True in listings. I think it looks better if we start a listing with alt = True (the dark one). Mon Feb 20 03:23:19 UTC 2006 Alberto Bertogli * Implement time logging. This record adds an option to log the time it took darcsweb to present a page. Unfortunately, because of the way it's implemented, it's impossible to isolate the time it takes to call darcs; however, every darcs invocation is logged too, to allow independant measures. Sun Feb 19 17:42:23 UTC 2006 Alberto Bertogli * Change cache file permissions 600. Change cache file permissions to 600. This should work on Windows too because the bits are ignored, according to python's documentation. Thu Jan 12 23:52:34 UTC 2006 Alberto Bertogli * Fix typos in the BOLA license. Mon Jan 9 22:25:04 UTC 2006 Alberto Bertogli tagged 0.14 Mon Jan 9 05:58:01 UTC 2006 Alberto Bertogli * Fix cache behaviour on uncaught exceptions. This patch make darcsweb cancel the cache when there is an uncaught exception, avoiding leaving dot-files around the cache directory. Also minimize DoS by not taking into account unused form parameters, and make the hash independant of the position. This doesn't eliminate all the opportunities for DoS, but reduces them significatively. Mon Jan 9 05:27:44 UTC 2006 Alberto Bertogli * Replace the darcs logo with a smaller one. Now that we have a search box, the logo becomes really big and distractive. Replace it with a smaller version, only with the ball. Mon Jan 9 05:24:26 UTC 2006 Alberto Bertogli * Implement a search box. This implements a small search box that appears on the right top besides the logo (which will be changed to a small version in a following patch). Sun Jan 8 01:45:32 UTC 2006 Alberto Bertogli * Add the BOLA license. darcsweb has been too long without an explicit license, it's time to add one. Enjoy! Sat Dec 31 04:16:51 UTC 2005 Leandro Lucarella * Silly changes to avoid HTTP redirects. Fri Dec 30 23:44:19 UTC 2005 Alberto Bertogli * Add autodesc and autoexclude options. This patch implements two optional options for multidir repos: autodesc and autoexclude. If autodesc is enabled, the description will be taken from the file named "_darcs/third_party/darcsweb/desc", inside the repository. If it doesn't exist, the default will be used. If autoexclude is enabled, only repositories with a directory named "_darcs/third_party/darcsweb/" will be listed. The path "_darcs/third_party/darcsweb/" for darcsweb-exclusive preferences was chosen following a suggestion by David Roundy in the mail with Subject "[darcs-users] darcsweb 0.12 and some questions", Message-ID 20051109123238.GB17288@abridgegame.org, date "9 Nov 2005 07:32:44". Fri Dec 30 21:08:43 UTC 2005 Alberto Bertogli * Escape filenames, just in case. In the same spirit as the last patch, add escape() around prints of file names. It's highly improbable, but it could happen for weird cases and it seems worth the effort. Tue Dec 27 03:19:14 UTC 2005 Michael Allan * Escape patch names. When patch names used in HTML, escape characters like '<'. Sat Dec 24 15:06:57 UTC 2005 Alberto Bertogli * Move the mimetypes import to the function. While it's a nice practise to put all imports above, in darcsweb's case it also increases startup latency. Because the mimetypes module is only used inside print_binary_header(), move the import inside the function. Wed Dec 21 22:24:47 UTC 2005 gaetan.lehmann@jouy.inra.fr * enhance mime type for binary files Wed Dec 21 15:00:33 UTC 2005 Alberto Bertogli * Identify binary files in plainblob. If we want to do a plainblob of a binary file, it's better to offer a proper download than to display it as text/plain. This patch identifies binary files (looking at darcs' information) and offers them for download with the proper name. Wed Dec 21 14:29:46 UTC 2005 Alberto Bertogli * Indentation and comment changes to multidir_deep. Tue Dec 20 16:30:22 UTC 2005 nils@ndecker.de * Add deep recursion option to multidir configuration Wed Dec 21 13:47:50 UTC 2005 Alberto Bertogli * Indentation changes to the uncompression patch. Tue Dec 20 11:36:01 UTC 2005 nils@ndecker.de * show raw for noncompressed patches Mon Dec 19 17:42:02 UTC 2005 Alberto Bertogli tagged 0.13 Mon Dec 19 17:30:50 UTC 2005 Alberto Bertogli * Shorten the author string in shortlog. Tue Dec 13 14:12:44 UTC 2005 Alberto Bertogli * Small changes to do_atom(), make it validate. Thu Dec 1 22:07:08 UTC 2005 stephane@sources.org * ATOM support for syndication Tue Dec 13 13:28:49 UTC 2005 Alberto Bertogli * Use darcsweb.cgi's modification time to invalidate cache entries. Besides the repository inventory modification time, we also want to check the script's modification time, so if we make changes to darcsweb (updating it, for instance), there's no possibility of serving stale pages if the user forgets to clean the cache. You still need to clean it for configuration changes. Wed Nov 30 11:36:26 UTC 2005 Kirill Smelkov * fixu8: honour config.repoencoding when decoding characters like [_\e3] Sat Nov 12 23:59:16 UTC 2005 Alexandre Rossi * another basic validation issue The issue was that there was an empty line before the XML declaration, which is not valid. Thu Nov 17 14:41:33 UTC 2005 Alberto Bertogli * Make how_old() return a fixed date when caching is enabled. how_old() becomes a problem when we have a cache, because the relative dates will get stalled in the cache. This is makes a workaround by making how_old() return a string containing the fixed date, which isn't quite nice but it will do the trick until a better solution comes up. Thu Nov 17 14:40:27 UTC 2005 Alberto Bertogli * Don't close the file in a cache miss. Thu Nov 17 03:27:57 UTC 2005 Alberto Bertogli * Call cache.cancel() only if we have a cache. Thu Nov 17 03:10:28 UTC 2005 Alberto Bertogli * Implement a simple cache. This patch implements a very simple but effective cache, so darcsweb can avoid regenerating everything all the time. Based on an idea from Alexandre Rossi. Thu Nov 10 15:22:39 UTC 2005 Leandro Lucarella * Use a more human-friendly format por list configuration variables. Thu Nov 10 00:34:34 UTC 2005 Alberto Bertogli * Highlight tags in the shortlog/summary. Wed Nov 9 23:59:47 UTC 2005 Alberto Bertogli * Add exclusion lists to multidir configuration. Wed Nov 9 01:10:31 UTC 2005 Alberto Bertogli tagged 0.12 Wed Nov 9 00:57:34 UTC 2005 Alberto Bertogli * Consider only directories with "_darcs" inside for multidir entries. In multidir configuration entries, skip all the directories that don't have a "_darcs" inside. Wed Nov 9 00:43:55 UTC 2005 Alberto Bertogli * Extend escape() to escape '"' too. Wed Nov 9 00:30:02 UTC 2005 Alberto Bertogli * Don't show the owner if there is none Wed Nov 9 00:23:06 UTC 2005 Alberto Bertogli * Add some checks to the multidir handling. Check if the entry is a directory and doesn't begin with a dot. Besides, sort it so the listing appears in predictable order. Wed Nov 9 00:17:31 UTC 2005 Alberto Bertogli * Implement "multidir" configuration entries. This patch implements a special configuration entry called "multidir" that can be useful when you have a lot of repositories in the same directory and don't want to create an entry for each one. Mon Oct 31 10:24:23 UTC 2005 Michal Sojka * Fix for an error that appears when run on empty repository. Sun Nov 6 23:48:38 UTC 2005 Alberto Bertogli * Show the patch name in the description in do_commit(). Mon Oct 31 00:11:03 UTC 2005 Alberto Bertogli * Simplify the "Verbose Annotate Sidebar" patch. This patch makes it simpler, because it skips displaying the full author name and how long ago the changes were made; besides some simple fixes. The rationale behind this is purely aesthetic: the author is already available in the tooltip (and you should be able to tell anyway by its nickname), and the "how long ago" was a bit distracting. All this is completely subjective, I know, but I think these are "safe" settings. Thu Sep 29 22:09:49 UTC 2005 Max Battcher * Verbose Annotate Sidebar In shade or zebra annotate modes, this patch displays a second and third line of information in a contiguous single patch code block. :First Line: ``DATE AUTHOR_SHORT_NAME`` (Same as before) :Second Line: ``TIME RELATIVE_TIME`` :Third Line: ``FULL_AUTHOR_EMAIL`` All following lines in the code block are blank. Sun Oct 30 23:23:47 UTC 2005 Alberto Bertogli * Include a list of changed files in each RSS description. Sun Oct 30 21:54:41 UTC 2005 Alberto Bertogli * Add full name as a tooltip in shortlog. Thu Oct 27 10:02:09 UTC 2005 Alexandre Rossi * css: cleared table to avoid cell wrap due to logo Wed Oct 26 12:28:15 UTC 2005 Alexandre Rossi * fixed small validation issue in XML header Mon Oct 3 04:57:19 UTC 2005 Alberto Bertogli * Put a '/' after the path in do_tree(). Mon Oct 3 04:51:44 UTC 2005 Alberto Bertogli * Make shade the default annotate style. Mon Oct 3 04:47:34 UTC 2005 Alberto Bertogli * Use the real file if there's no pristine directory available. Wed Sep 21 18:36:17 UTC 2005 Alberto Bertogli * Handle plain emails in annotate's author parsing. Wed Sep 21 18:24:58 UTC 2005 Alberto Bertogli * Shade and zebra styles for annotate. This patch adds two more annotate styles: zebra and shade. Zebra alternates the line background colour according to the patch, just like most CVS viewers do. Shade is a lot more fun: it gives each patch a shade of a colour, the newer the brighter, so newer patches (which are often the ones you're intrested in) are easier to spot. It could use a little tuning on some constants to make it look better, but it's pretty usable as is, and it can be done later if needed. Thanks to Max Battcher for the idea. Wed Sep 21 05:51:22 UTC 2005 Alberto Bertogli * Gather more information from annotate parsing. Add a list of patches with their dates to the annotate object, because we'll want to use them in patches to come. Also store the date in tuple format and let the users do the conversion to text as they need to. Tue Sep 20 18:46:54 UTC 2005 Alberto Bertogli tagged 0.11 Tue Sep 20 18:13:33 UTC 2005 Alberto Bertogli * Indent a comment. Yeah, I feel stupid creating a patch just for this, but my 80-column terminal just hurts when I look at this line. Tue Sep 20 07:40:25 UTC 2005 Alberto Bertogli * Add patch description to print_annotate(). Add a column to the left of the annotate output, just before the line number, to show the date (but not time) and author (in a short form). Tue Sep 20 07:31:31 UTC 2005 Alberto Bertogli * Replace tabs in annotate output. Tue Sep 20 07:01:50 UTC 2005 Alberto Bertogli * Replace tabs with spaces for nicer output. Tabs need to be replaced with spaces to avoid problems displaying lines that begin with tabs. This patch implements a very simple function to do so, and modifices print_blob() to use it. Mon Sep 19 06:30:38 UTC 2005 Alberto Bertogli * Use UTF8 for plain output. Make the plain outputs use UTF8 too, as it's the only sane option. Mon Sep 19 06:20:23 UTC 2005 Alberto Bertogli * Fix some UTF8 handling in annotate, and add tooltips. This patch fixes some of the UTF8 handling in different annotate parts. Accidentally, it also includes a small modification to show for each line of the annotate the last modification time, date and author as a tooltip. It'd be nice to put this in a different patch. Mon Sep 19 03:19:14 UTC 2005 Alberto Bertogli * Convert the annotate source to UTF8 before parsing. Mon Sep 12 13:13:54 UTC 2005 Toni Timonen * Fix validity problem with rss feeds and unknown email addresses. Mon Sep 19 02:50:04 UTC 2005 Alberto Bertogli * Implement annotate. Finally! Wed Sep 7 16:59:52 UTC 2005 Alberto Bertogli * Support replaces. Wed Sep 7 16:59:10 UTC 2005 Alberto Bertogli * Show the raw format in all commitdiff variants. Wed Sep 7 16:58:43 UTC 2005 Alberto Bertogli * Show the patch formats in the commit view too. Wed Sep 7 06:40:50 UTC 2005 Alberto Bertogli * Pass the filename to the navigation bar where it's missing. Wed Sep 7 06:25:28 UTC 2005 Alberto Bertogli * Support darcs-style diff output. Fri Aug 26 14:32:13 UTC 2005 Alberto Bertogli * Specify that the popen4 is text and not binary. popen4 call takes a mode parameter that should specify if the fds are text or binary. Thanks to Gordon Heydon for reporting it. Fri Aug 26 01:27:11 UTC 2005 Alberto Bertogli * Change some calls to darcs so it runs properly under Windows. This small changes makes darcsweb run properly under Windows. Thanks to Gordon Heydon who reported the bug and tested the fix. Wed Aug 24 18:38:04 UTC 2005 Alberto Bertogli * Add support for specification of summary and footer. This patch adds support to allow you to specify the summary shown in the repo list, as well as changing the default footer; all optional. This is based on the patches sent by Zachary P. Landau and Remko Troncon. Wed Aug 24 08:51:58 UTC 2005 Remko Troncon * Made darcspath example correcter and less easy to confuse. The darcspath example now ends in 'bin/', to indicate that the path is prepended to the 'darcs' command name. Wed Aug 24 00:31:14 UTC 2005 Alberto Bertogli * Remove the TODO file, it doesn't belong here. Wed Aug 24 00:20:18 UTC 2005 Alberto Bertogli tagged 0.10 Wed Aug 24 00:08:54 UTC 2005 Alberto Bertogli * Fix RSS title timestamp. Fri Aug 12 01:56:59 UTC 2005 Mark Stosberg * typo fix diff -rN -u old-darcsweb/LICENSE new-darcsweb/LICENSE --- old-darcsweb/LICENSE 1970-01-01 00:00:00.000000000 +0000 +++ new-darcsweb/LICENSE 2013-08-05 23:01:35.000000000 +0000 @@ -0,0 +1,34 @@ + +I don't like licenses, because I don't like having to worry about all this +legal stuff just for a simple piece of software I don't really mind anyone +using. But I also believe that it's important that people share and give back; +so I'm placing darcsweb under the following license, to make you feel guilty +if you don't ;) + + +BOLA - Buena Onda License Agreement +----------------------------------- + +This work is provided 'as-is', without any express or implied warranty. In no +event will the authors be held liable for any damages arising from the use of +this work. + +To all effects and purposes, this work is to be considered Public Domain. + + +However, if you want to be "Buena onda", you should: + +1. Not take credit for it, and give proper recognition to the authors. +2. Share your modifications, so everybody benefits from them. +4. Do something nice for the authors. +5. Help someone who needs it: sign up for some volunteer work or help your + neighbour paint the house. +6. Don't waste. Anything, but specially energy that comes from natural + non-renewable resources. Extra points if you discover or invent something + to replace them. +7. Be tolerant. Everything that's good in nature comes from cooperation. + +The order is important, and the further you go the more "Buena onda" you are. +Make the world a better place: be "Buena onda". + + diff -rN -u old-darcsweb/README new-darcsweb/README --- old-darcsweb/README 2013-08-05 23:01:35.000000000 +0000 +++ new-darcsweb/README 2013-08-05 23:01:35.000000000 +0000 @@ -1,7 +1,7 @@ darcsweb - A web interface for darcs -Alberto Bertogli (albertogli@telpin.com.ar) -------------------------------------------- +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/TODO new-darcsweb/TODO --- old-darcsweb/TODO 2013-08-05 23:01:35.000000000 +0000 +++ new-darcsweb/TODO 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ - -* 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 2013-08-05 23:01:35.000000000 +0000 +++ new-darcsweb/config.py.sample 2013-08-05 23:01:35.000000000 +0000 @@ -1,12 +1,6 @@ # 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" @@ -16,16 +10,90 @@ # the CSS file to use cssfile = 'style.css' + # this script's name, usually just "darcsweb.cgi" unless you rename + # it; if you leave this commented it will be detected automatically + #myname = "darcsweb.cgi" + + # our url, used only to generate RSS links, without the script name; + # if you leave this commented it will be detected automatically + #myurl = "http://example.com/darcsweb" + # optionally, you can specify the path to the darcs executable; if you # leave this commented, the one on $PATH will be used (this is # normally what you want) - #darcspath = "/home/me/bin/darcs" + #darcspath = "/home/me/bin/" + + # the text to appear in the top of repo list; this is also optional, + # and html-formatted + #summary = "I love darcs!" + + # in case you want to change the beautiful default, you can specify an + # alternative footer here; it's optional, of course + #footer = "I don't like shoes" + + # It is possible to have a cache where darcsweb will store the pages + # it generates; entries are automatically updated when the repository + # changes. This will speed things up significatively, specially for + # popular sites. + # It's recommended that you clean the directory with some regularity, + # to avoid having too many unused files. A simple rm will do just + # fine. + # If you leave the entry commented, no cache will be ever used; + # otherwise the directory is assumed to exist and be writeable. + # If you use this option you must set the "myname" and "myurl" + # variables. + #cachedir = '/tmp/darcsweb-cache' + + # By default, darcsweb's search looks in the last 100 commits; you can + # change that number by specifying it here. + # Note that search are not cached, so if you have tons of commits and + # set the limit to a very high number, they will take time. + #searchlimit = 100 + + # If you want to log the times it took darcsweb to present a page, + # uncomment this option. The value should be a file writeable by + # darcsweb. + #logtimes = "/tmp/darcsweb_times" + + # If you want darcsweb to automatically detect embedded URLs, + # define them here, using python-style regexps like the examples + # below. They will be replaced in summaries, logs, and commits. + # The following variables are replaced: + # myreponame: repository link (darcsweb.cgi?r=repo) + # reponame: repository name (repo) + # + #url_links = ( + # Format is: (regexp, replacement) + # Some examples: + # + # Detect '#NNN' as a reference to bug database + #(r'#([0-9]+)', + # r'#\1'), + # + # Replace hashes with commit-links. + #(r'(\d{14}-[0-9a-f]{5}-[0-9a-f]{40}\.gz)', + # r'\1'), + #) + + # If you want to generate links from patch author names, define the url + # here. Example for CIA: + #author_links = "http://cia.navi.cx/stats/author/%(author)s" + + # If you want to disable the annotate feature (for performance reasons, + # the http connection will time out on slow machines), uncomment this + # 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' # # 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). +# "base" (because it's the name of the one above). # # If you have a lot of repos and/or you're too lazy to do this by hand, you # can use the configuration generator that comes with darcsweb, called @@ -43,21 +111,89 @@ repodir = '/usr/src/repo1' # an url so people know where to do "darcs get" from - repourl = 'http://auriga.wearlab.de/~alb/repos/repo1' + repourl = 'http://example.com/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. + # You can, optionally, specify multiple encodings; they're tried in + # order, and if one fails to decode a string, the next one is tried. + # Example: repoencoding = "utf8", "latin1" repoencoding = "latin1" + # as with the base configuration, the footer is also optional, and it + # affects only this repository; if you don't specify, the one + # specified in base is used (and if you don't specify one there + # either, a default one is used) + #footer = "I don't like being cold" + + # Each repository may show a link to some website associated with it. + # This is typically useful if you've got a website describing the + # software in your repository. + #repoprojurl = 'http://example.com/projects/repo1/' + class repo2: reponame = 'repo2' repodesc = 'Second example repository' repodir = '/usr/src/repo2' - repourl = 'http://auriga.wearlab.de/~alb/repos/repo2' + repourl = 'http://example.com/repos/repo2/' + repoencoding = 'latin1' + + +# +# If you have several repositories in a single directory and don't want to +# 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. 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 +# with a dot (.) are not searched. This may be slow, if huge directory trees +# must be searched. It's unnecesary unless you have a multidir with several +# nested repositories. It defaults to False, and it's optional. +# + +class multi1: + multidir = '/usr/local/src' + #multidir_deep = False + repodesc = 'Repository for %(name)s' + 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/' + + # if you want to exclude some directories, add them to this list (note + # they're relative to multidir, not absolute) + #exclude = 'dir1', 'dir2' + + # if you want the descriptions to be picked up automatically from the + # file named "_darcs/third_party/darcsweb/desc" (one line only), set + # this to True. It defaults to False + #autodesc = True + + # if you want to exclude all the repositories which do NOT have a + # directory named "_darcs/third_party/darcsweb/" inside, set this to + # True. It defaults to False. + #autoexclude = True + + # if you want urls to be picked up automatically from the file named + # "_darcs/third_party/darcsweb/url" (one line only), set this to + # True. It defaults to False. + #autourl = True + + # if you want the projects urls to be picked up automatically from the + # file named "_darcs/third_party/darcsweb/projurl" (one line only), set + # this to True. It defaults to False. + #autoprojurl = True 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 2013-08-05 23:01:35.000000000 +0000 +++ new-darcsweb/darcsweb.cgi 2013-08-05 23:01:35.000000000 +0000 @@ -2,28 +2,59 @@ """ darcsweb - A web interface for darcs -Alberto Bertogli (albertogli@telpin.com.ar) +Alberto Bertogli (albertito@blitiri.com.ar) Inspired on gitweb (as of 28/Jun/2005), which is written by Kay Sievers and Christian Gierke """ +import time +time_begin = time.time() import sys import os import string -import time import stat import cgi import cgitb; cgitb.enable() import urllib import xml.sax -from xml.sax.saxutils import escape +from xml.sax.saxutils import escape as xml_escape +time_imports = time.time() - time_begin + +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. 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: pass +# list of run_darcs() invocations, for performance measures +darcs_runs = [] + +# exception handling +def exc_handle(t, v, tb): + try: + cache.cancel() + except: + pass + cgitb.handler((t, v, tb)) +sys.excepthook = exc_handle # # utility functions @@ -31,24 +62,24 @@ def filter_num(s): l = [c for c in s if c in string.digits] - return string.join(l, "") + return ''.join(l) allowed_in_action = string.ascii_letters + string.digits + '_' def filter_act(s): l = [c for c in s if c in allowed_in_action] - return string.join(l, "") + return ''.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, "") + return ''.join(l) def filter_file(s): - if '..' in s: - raise 'FilterFile FAILED' + if '..' in s or '"' in s: + raise Exception, 'FilterFile FAILED' if s == '/': return s @@ -64,50 +95,47 @@ def printd(*params): - print string.join(params), '
' + print ' '.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 - if type(s) == unicode: - # workaround for python < 2.4 - return s.encode('utf8') - else: - 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') + """Calls _fixu8(), which does the real work, line by line. Otherwise + we choose the wrong encoding for big buffers and end up messing + output.""" + n = [] + for i in s.split('\n'): + n.append(_fixu8(i)) + return '\n'.join(n) + +def _fixu8(s): + if type(s) == unicode: + return s.encode('utf8', 'replace') + for e in config.repoencoding: + try: + return s.decode(e).encode('utf8', 'replace') + except UnicodeDecodeError: + pass + raise UnicodeDecodeError, config.repoencoding + + +def escape(s): + s = xml_escape(s) + s = s.replace('"', '"') return s - def how_old(epoch): + if config.cachedir: + # when we have a cache, the how_old() becomes a problem since + # the cached entries will have old data; so in this case just + # return a nice string + t = time.localtime(epoch) + 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: s = str(age/60/60/24/365) @@ -139,6 +167,52 @@ s = s[:max - 4] + ' ...' return s +def replace_tabs(s): + pos = s.find("\t") + while pos != -1: + count = 8 - (pos % 8) + if count: + spaces = ' ' * count + s = s.replace('\t', spaces, 1) + pos = s.find("\t") + return s + +def replace_links(s): + """Replace user defined strings with links, as specified in the + configuration file.""" + import re + + vardict = { + "myreponame": config.myreponame, + "reponame": config.reponame, + } + + for link_pat, link_dst in config.url_links: + s = re.sub(link_pat, link_dst % vardict, s) + + 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" + import re + # build the regexp by leaving "(s)", replacing '(' and ') first + s = s.replace('\\', '\\\\') + s = s.replace('(', '\\(') + s = s.replace(')', '\\)') + s = '(' + escape(s) + ')' + try: + pat = re.compile(s, re.I) + repl = '\\1' + l = re.sub(pat, repl, l) + except: + pass + return l + def fperms(fname): m = os.stat(fname)[stat.ST_MODE] m = m & 0777 @@ -167,8 +241,16 @@ if m & 0001: s.append('x') else: s.append('-') - return string.join(s, '') + return ''.join(s) +def fsize(fname): + s = os.stat(fname)[stat.ST_SIZE] + if s < 1024: + return "%s" % s + elif s < 1048576: + return "%sK" % (s / 1024) + elif s < 1073741824: + return "%sM" % (s / 1048576) def isbinary(fname): import re @@ -179,20 +261,76 @@ return 1 return 0 +def realpath(fname): + realf = filter_file(config.repodir + '/_darcs/pristine/' + fname) + if os.path.exists(realf): + return realf + realf = filter_file(config.repodir + '/_darcs/current/' + fname) + if os.path.exists(realf): + return realf + realf = filter_file(config.repodir + '/' + fname) + return realf + +def log_times(cache_hit, repo = None, event = None): + if not config.logtimes: + return + + time_total = time.time() - time_begin + processing = time_total - time_imports + if not event: + event = action + if cache_hit: + event = event + " (hit)" + s = '%s\n' % event + + if repo: + s += '\trepo: %s\n' % repo + + s += """\ + total: %.3f + processing: %.3f + imports: %.3f\n""" % (time_total, processing, time_imports) + + if darcs_runs: + s += "\truns:\n" + for params in darcs_runs: + s += '\t\t%s\n' % params + s += '\n' + + lf = open(config.logtimes, 'a') + lf.write(s) + 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 # def print_header(): - print "Content-type: text/html; charset=utf-8\n" + print "Content-type: text/html; charset=utf-8" print """ - + darcs repository (several authors) + darcsweb.cgi + %(lastmod)s + %(desc)s + """ % { + 'reponame': config.reponame, + 'url': config.myurl + '/' + config.myreponame, + 'desc': escape(config.repodesc), + 'lastmod': str_lastmod, + } + + ps = get_last_patches(20) + for p in ps: + title = time.strftime('%d %b %H:%M', time.localtime(p.date)) + title += ' - ' + p.name + pdate = time.strftime(iso_datetime, + time.localtime(p.date)) + link = '%s/%s;a=commit;h=%s' % (config.myurl, + config.myreponame, p.hash) + + import email.Utils + addr, author = email.Utils.parseaddr(p.author) + if not addr: + addr = "unknown_email@example.com" + if not author: + author = addr + + print """ + + %(title)s + + %(author)s + %(email)s + + %(pdate)s + %(link)s + + %(desc)s +

+ """ % { + 'title': escape(title), + 'author': author, + 'email': addr, + 'url': config.myurl + '/' + config.myreponame, + 'pdate': pdate, + 'myrname': config.myreponame, + 'hash': p.hash, + 'pname': escape(p.name), + 'link': link, + 'desc': escape(p.name), + } + + # TODO: allow to get plain text, not HTML? + print escape(p.name) + '
' + if p.comment: + print '
' + print escape(p.comment).replace('\n', '
\n') + print '
' + print '
' + changed = p.adds + p.removes + p.modifies.keys() + \ + p.moves.keys() + p.diradds + p.dirremoves + \ + p.replaces.keys() + for i in changed: # TODO: link to the file + print '%s
' % i + print '

' + print '
' + print '' def do_rss(): print "Content-type: text/xml; charset=utf-8\n" @@ -1186,12 +2162,12 @@ """ % { 'reponame': config.reponame, 'url': config.myurl + '/' + config.myreponame, - 'desc': config.repodesc, + 'desc': escape(config.repodesc), } ps = get_last_patches(20) for p in ps: - title = time.strftime('%d %b %H:%S', time.localtime(p.date)) + title = time.strftime('%d %b %H:%M', time.localtime(p.date)) title += ' - ' + p.name pdate = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime(p.date)) @@ -1205,7 +2181,7 @@ if "@" in p.author: author = p.author else: - author = "%s " % p.author + author = "%s <unknown@email>" % p.author print """ @@ -1226,26 +2202,82 @@ if p.comment: print '
' print escape(p.comment).replace('\n', '
\n') + print '
' + print '
' + changed = p.adds + p.removes + p.modifies.keys() + \ + p.moves.keys() + p.diradds + p.dirremoves + \ + p.replaces.keys() + for i in changed: + print '%s
' % i print ']]>' print '
' print '' +def do_search(s): + print_header() + print_navbar() + ps = get_last_patches(config.searchlimit) + + print '
Search last %d commits for "%s"
' \ + % (config.searchlimit, escape(s)) + print '' + + alt = True + for p in ps: + match = p.matches(s) + if not match: + continue + + if alt: + print '' + else: + print '' + alt = not alt + + print """ + + + + + """ % { + 'age': how_old(p.local_date), + 'author': gen_authorlink(p.author, shorten_str(p.shortauthor, 26)), + 'myrname': config.myreponame, + 'hash': p.hash, + 'name': escape(shorten_str(p.name)), + 'fullname': escape(p.name), + 'match': highlight(s, shorten_str(match)), + } + print "" + + print '
%(age)s%(author)s + + %(name)s +
+ %(match)s +
' + print_footer() + + def do_die(): print_header() print "

Error! Malformed query

" print_footer() - sys.exit(1) def do_listrepos(): import config as all_configs + expand_multi_config(all_configs) # the header here is special since we don't have a repo print "Content-type: text/html; charset=utf-8\n" + print '' print """ - @@ -1266,25 +2298,29 @@ repos / index
-This is the repository index for a darcsweb site.
-These are all the available repositories.
+%(summary)s
+ + """ % { - 'myname': all_configs.base.myname, - 'css': all_configs.base.cssfile, - 'fav': all_configs.base.darcsfav, - 'logo': all_configs.base.darcslogo + 'myname': config.myname, + 'css': config.cssfile, + 'fav': config.darcsfav, + 'logo': config.darcslogo, + 'summary': config.summary } # some python magic - alt = False - for conf in dir(all_configs): + alt = True + confs = dir(all_configs) + confs.sort(key=str.lower) + for conf in confs: if conf.startswith('__'): continue c = all_configs.__getattribute__(conf) @@ -1296,9 +2332,14 @@ if alt: print '' else: print '' alt = not alt + try: orig_repodir = config.repodir + except: orig_repodir = None + config.repodir = c.repodir print """ + + """ % { - 'myname': all_configs.base.myname, + '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 "
Project DescriptionOwnerLast Change
%(dname)s %(desc)s%(owner)s%(lastchange)s
" print_footer(put_rss = 0) -def fill_config(name): - import config as all_configs - for conf in dir(all_configs): +def expand_multi_config(config): + """Expand configuration entries that serve as "template" to others; + this make it easier to have a single directory with all the repos, + because they don't need specific entries in the configuration anymore. + """ + + for conf in dir(config): if conf.startswith('__'): continue - c = all_configs.__getattribute__(conf) - if 'reponame' not in dir(c): + c = config.__getattribute__(conf) + if 'multidir' not in dir(c): continue - if c.reponame == name: - break - else: - # not found - raise "RepoNotFound", name + + if not os.path.isdir(c.multidir): + continue + + if 'exclude' not in dir(c): + c.exclude = [] + + entries = [] + if 'multidir_deep' in dir(c) and c.multidir_deep: + for (root, dirs, files) in os.walk(c.multidir): + # do not visit hidden directories + dirs[:] = [d for d in dirs \ + if not d.startswith('.')] + if '_darcs' in dirs: + p = root[1 + len(c.multidir):] + entries.append(p) + else: + entries = os.listdir(c.multidir) + + entries.sort() + for name in entries: + name = name.replace('\\', '/') + if name.startswith('.'): + continue + fulldir = c.multidir + '/' + name + if not os.path.isdir(fulldir + '/_darcs'): + continue + 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' + if not os.path.isdir(dpath): + continue + + if 'autodesc' in dir(c) and c.autodesc: + dpath = fulldir + \ + '/_darcs/third_party/darcsweb/desc' + if os.access(dpath, os.R_OK): + desc = open(dpath).readline().rstrip("\n") + else: + desc = c.repodesc % rep_dict + else: + desc = c.repodesc % rep_dict + + if 'autourl' in dir(c) and c.autourl: + dpath = fulldir + \ + '/_darcs/third_party/darcsweb/url' + if os.access(dpath, os.R_OK): + url = open(dpath).readline().rstrip("\n") + else: + url = c.repourl % rep_dict + else: + url = c.repourl % rep_dict + + if 'autoprojurl' in dir(c) and c.autoprojurl: + dpath = fulldir + \ + '/_darcs/third_party/darcsweb/projurl' + if os.access(dpath, os.R_OK): + projurl = open(dpath).readline().rstrip("\n") + elif 'repoprojurl' in dir(c): + projurl = c.repoprojurl % rep_dict + else: + projurl = None + elif 'repoprojurl' in dir(c): + 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 = dname + repodir = rdir + repodesc = desc + repourl = url + repoencoding = c.repoencoding + repoprojurl = projurl + repolisturl = listurl + + if 'footer' in dir(c): + footer = c.footer + + # index by display name to avoid clashes + config.__setattr__(dname, tmp_config) + +def fill_config(name = None): + import config as all_configs + expand_multi_config(all_configs) + + if name: + # we only care about setting some configurations if a repo was + # specified; otherwise we only set the common configuration + # directives + 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 Exception, "Repo not found: " + repr(name) # fill the configuration base = all_configs.base - config.myname = base.myname - config.myreponame = base.myname + '?r=' + urllib.quote(name) - config.myurl = base.myurl + if 'myname' not in dir(base): + # SCRIPT_NAME has the full path, we only take the file name + config.myname = os.path.basename(os.environ['SCRIPT_NAME']) + else: + config.myname = base.myname + + if 'myurl' not in dir(base): + n = os.environ['SERVER_NAME'] + p = os.environ['SERVER_PORT'] + 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%s' % (u and 's' or '', n, p, s) + else: + 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 + if name: + config.myreponame = config.myname + '?r=' + urllib.quote(name) + config.reponame = c.reponame + config.repodesc = c.repodesc + config.repodir = c.repodir + config.repourl = c.repourl + + config.repoprojurl = None + 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, ) + else: + config.repoencoding = c.repoencoding # optional parameters if "darcspath" in dir(base): @@ -1348,16 +2550,72 @@ else: config.darcspath = "" + if "summary" in dir(base): + config.summary = base.summary + else: + config.summary = """ +This is the repository index for a darcsweb site.
+These are all the available repositories.
+ """ + + if "cachedir" in dir(base): + config.cachedir = base.cachedir + else: + config.cachedir = None + + if "searchlimit" in dir(base): + config.searchlimit = base.searchlimit + else: + config.searchlimit = 100 + + if "logtimes" in dir(base): + config.logtimes = base.logtimes + else: + config.logtimes = None + + if "url_links" in dir(base): + config.url_links = base.url_links + else: + config.url_links = () + + if name and "footer" in dir(c): + config.footer = c.footer + elif "footer" in dir(base): + config.footer = base.footer + else: + config.footer = "Crece desde el pueblo el futuro / " \ + + "crece desde el pie" + if "author_links" in dir(base): + config.author_links = base.author_links + else: + config.author_links = None + if "disable_annotate" in dir(base): + config.disable_annotate = base.disable_annotate + else: + config.disable_annotate = False + + if "readme_converter" in dir(base): + config.readme_converter = base.readme_converter + else: + config.readme_converter = False + + # # main # +if sys.version_info < (2, 3): + print "Sorry, but Python 2.3 or above is required to run darcsweb." + sys.exit(1) + form = cgi.FieldStorage() # if they don't specify a repo, print the list and exit if not form.has_key('r'): + fill_config() do_listrepos() + log_times(cache_hit = 0, event = 'index') sys.exit(0) # get the repo configuration and fill the config class @@ -1371,6 +2629,25 @@ else: action = filter_act(form["a"].value) +# check if we have the page in the cache +if config.cachedir: + 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', '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() + cache = Cache(config.cachedir, url_request) + if cache.open(): + # we have a hit, dump and run + cache.dump() + cache.close() + log_times(cache_hit = 1, repo = config.reponame) + sys.exit(0) + # if there is a miss, the cache will step over stdout, intercepting + # all "print"s and writing them to the cache file automatically + # see what should we do according to the received action if action == "summary": @@ -1412,7 +2689,8 @@ do_plain_filediff(phash, fname) elif action == "darcs_filediff": phash = filter_hash(form["h"].value) - do_darcs_filediff(phash) + fname = filter_file(form["f"].value) + do_darcs_filediff(phash, fname) elif action == 'headfilediff': phash = filter_hash(form["h"].value) @@ -1424,15 +2702,48 @@ do_plain_fileheaddiff(phash, fname) elif action == "darcs_headfilediff": phash = filter_hash(form["h"].value) - do_darcs_fileheaddiff(phash) + fname = filter_file(form["f"].value) + do_darcs_fileheaddiff(phash, fname) +elif action == "annotate_normal": + fname = filter_file(form["f"].value) + if form.has_key("h"): + phash = filter_hash(form["h"].value) + else: + phash = None + do_annotate(fname, phash, "normal") +elif action == "annotate_plain": + fname = filter_file(form["f"].value) + if form.has_key("h"): + phash = filter_hash(form["h"].value) + else: + phash = None + do_annotate_plain(fname, phash) +elif action == "annotate_zebra": + fname = filter_file(form["f"].value) + if form.has_key("h"): + phash = filter_hash(form["h"].value) + else: + phash = None + do_annotate(fname, phash, "zebra") +elif action == "annotate_shade": + fname = filter_file(form["f"].value) + if form.has_key("h"): + phash = filter_hash(form["h"].value) + else: + phash = None + do_annotate(fname, phash, "shade") elif action == "shortlog": if form.has_key("topi"): 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"): @@ -1440,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) @@ -1467,8 +2786,28 @@ elif action == 'rss': do_rss() +elif action == 'atom': + do_atom() + +elif action == 'search': + if form.has_key('s'): + s = form["s"].value + else: + s = '' + do_search(s) + if config.cachedir: + cache.cancel() + else: action = "invalid query" do_die() + if config.cachedir: + cache.cancel() + + +if config.cachedir: + cache.close() + +log_times(cache_hit = 0, repo = config.reponame) diff -rN -u old-darcsweb/style.css new-darcsweb/style.css --- old-darcsweb/style.css 2013-08-05 23:01:35.000000000 +0000 +++ new-darcsweb/style.css 2013-08-05 23:01:35.000000000 +0000 @@ -1,6 +1,6 @@ /* darcsweb CSS - * Alberto Bertogli (albertogli@telpin.com.ar) + * Alberto Bertogli (albertito@blitiri.com.ar) * * Copied almost entirely from gitweb's, written by Kay Sievers * and Christian Gierke . @@ -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,11 +59,25 @@ } div.page_body { - padding:8px; + padding:0.5em; +} + +div.search_box { + float:right; + text-align:right; +} + +input.search_text { + font-size:0.66em; + background-color: #edece6; +} + +input.search_button { + 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; @@ -82,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; @@ -125,43 +133,56 @@ color:#880000; } +a.line { + text-decoration:none; + color:#000000; +} + +a.line:hover { + text-decoration:none; + color:#880000; +} + table { - padding:8px 4px; + /*clear:both;*/ + padding:0.5em 0.25em; } th { - padding:2px 5px; - font-size:12px; + padding:0.25em 0.5em; text-align:left; } -tr.light:hover { +.light:hover { background-color:#edece6; } -tr.dark { +.dark { background-color:#f6f6f0; } -tr.dark:hover { +.dark:hover { background-color:#edece6; } +.tag { + background-color:#f0f0ff; +} + +.tag:hover { + background-color:#e0e0ff; +} + 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; } @@ -182,26 +203,32 @@ margin:4px 8px; position:absolute; top:56px; - right:12px + right:12px; } -a.linenr { +a.linenr, .linenos { color:#999999; - text-decoration:none + text-decoration:none; +} + +a.annotate_desc { + color:#999999; + text-decoration:none; +} + +a.annotate_desc:hover { + color:#880000; } 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; } @@ -210,3 +237,77 @@ background-color:#ee5500; } +img.logo { + border-width:0; + vertical-align:top; + 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 */