diff --git a/docs/.gitignore b/docs/.gitignore
index 80bf00fe71172fbf91ef27b9def2e19e81d62d5b..dd78dd0d84d79db9656b40739c409378e2c7bfdb 100644
--- a/docs/.gitignore
+++ b/docs/.gitignore
@@ -5,3 +5,4 @@ apibuild.pyc
*.html
libvirt-api.xml
libvirt-refs.xml
+todo.html.in
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 7fae40db83f62427f23cdf6407888a7ef18330b5..674c443f745497bf3c4a78b008940cc109ad4d64 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -1,6 +1,8 @@
## Process this file with automake to produce Makefile.in
SUBDIRS= schemas
+PERL = perl
+
# The directory containing the source code (if it contains documentation).
DOC_SOURCE_DIR=../src
@@ -53,7 +55,7 @@ gif = \
architecture.gif \
node.gif
-dot_html_in = $(notdir $(wildcard $(srcdir)/*.html.in))
+dot_html_in = $(notdir $(wildcard $(srcdir)/*.html.in)) todo.html.in
dot_html = $(dot_html_in:%.html.in=%.html)
patches = $(wildcard api_extension/*.patch)
@@ -97,6 +99,22 @@ ChangeLog.html.in: ChangeLog.xml ChangeLog.xsl
$(XSLTPROC) --nonet $(top_srcdir)/docs/ChangeLog.xsl $< > $@ \
|| { rm $@ && exit 1; }; fi
+todo.html.in: todo.pl
+ if [ -f todo.cfg ]; then \
+ echo "Generating $@"; \
+ $(PERL) $(srcdir)/$< > $@ \
+ || { rm $@ && exit 1; }; \
+ else \
+ echo "Stubbing $@"; \
+ echo "
Todo list
" > $@ ; \
+ fi
+
+todo:
+ rm -f todo.html.in
+ $(MAKE) todo.html
+
+.PHONY: todo
+
%.png: %.fig
convert -rotate 90 $< $@
@@ -153,7 +171,7 @@ clean-local:
rm -f *~ *.bak *.hierarchy *.signals *-unused.txt *.html
maintainer-clean-local: clean-local
- rm -rf libvirt-api.xml libvirt-refs.xml
+ rm -rf libvirt-api.xml libvirt-refs.xml todo.html.in
rebuild: api all
diff --git a/docs/sitemap.html.in b/docs/sitemap.html.in
index bb9cd8e82e1812e8247a4d2d3c32f6c094a1102e..481507e670c117e51b63d3d5470a0df481cb8d74 100644
--- a/docs/sitemap.html.in
+++ b/docs/sitemap.html.in
@@ -275,6 +275,12 @@
Bug reports
How and where to report bugs and request features
+
+ -
+ Todo list
+ Main feature request list
+
+
Contact
diff --git a/docs/todo.cfg-example b/docs/todo.cfg-example
new file mode 100644
index 0000000000000000000000000000000000000000..a99c61ae4b18aea8aef6da66e54ab93e1f63cd59
--- /dev/null
+++ b/docs/todo.cfg-example
@@ -0,0 +1,26 @@
+bugzilla = {
+ #username = ...some email addr...
+ #password = ...some bz password...
+ server = https://bugzilla.redhat.com
+}
+query = {
+ product = Virtualization Tools
+ alias = libvirtTodo
+}
+
+output = {
+ title = Todo list
+ blurb = <development mailing list.
+If you intend to work on one of the items here, it is recommended to
+add a comment to the corresponding bugzilla ticket to this effect.
+This will reduce the risk of duplicated effort between developers.
+It is also strongly recommended to mail the development mailing list
+with proposals for new APIs & XML before fully implementing them, to
+allow for early design review.
+EOF
+}
diff --git a/docs/todo.pl b/docs/todo.pl
new file mode 100755
index 0000000000000000000000000000000000000000..5db940fcaeb343f8353dfb1612be9bc8497748e8
--- /dev/null
+++ b/docs/todo.pl
@@ -0,0 +1,120 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use BZ::Client;
+use BZ::Client::Bug;
+
+use Config::Record;
+
+my $cfg = Config::Record->new(file => "todo.cfg");
+my $server = $cfg->get("bugzilla/server", "https://bugzilla.redhat.com");
+my $username = $cfg->get("bugzilla/username");
+my $password = $cfg->get("bugzilla/password");
+
+my $product = $cfg->get("query/product", "Virtualization Tools");
+my $todoalias = $cfg->get("query/todoalias", "libvirtTodo");
+
+my $title = $cfg->get("output/title", undef);
+my $blurb = $cfg->get("output/blurb", undef);
+
+$SIG{__DIE__} = sub {
+ my $err = shift;
+ if (UNIVERSAL::isa($err, "BZ::Client::Exception")) {
+ die "Unable to access bugzilla: " . $err->message;
+ }
+ die $err;
+};
+
+my $client = BZ::Client->new(url => $server,
+ user => $username,
+ password => $password);
+
+my $todo = BZ::Client::Bug->search($client, {'product' => $product,
+ 'alias' => $todoalias});
+
+die "Cannot find bug alias 'libvirtTodo'" unless $#{$todo} > -1;
+my $todoid = $todo->[0]->{'bug_id'};
+my $todosummary = $todo->[0]->{'short_desc'};
+$todosummary =~ s/^\s*RFE\s*:\s*//;
+$todosummary =~ s/^\s*\[\s*RFE\s*\]\s*:?\s*//;
+$todosummary =~ s/^\s*Tracker\s*:\s*//;
+
+my $trackers = BZ::Client::Bug->search($client, {'product' => $product,
+ 'blocked' => $todoid });
+
+my @trackers;
+
+foreach my $tracker (@{$trackers}) {
+ my $summary = $tracker->{'short_desc'};
+ $summary =~ s/^\s*RFE\s*:\s*//;
+ $summary =~ s/^\s*\[\s*RFE\s*\]\s*:?\s*//;
+ $summary =~ s/^\s*Tracker\s*:\s*//;
+
+ push @trackers, {
+ id => $tracker->{'bug_id'},
+ summary => $summary,
+ features => [],
+ };
+}
+
+foreach my $tracker (@trackers) {
+ my $features = BZ::Client::Bug->search($client, {'product' => $product,
+ 'blocked' => $tracker->{id}});
+
+ foreach my $feature (@{$features}) {
+ my $summary = $feature->{'short_desc'};
+ $summary =~ s/^\s*RFE\s*:\s*//;
+ $summary =~ s/^\s*\[\s*RFE\s*\]\s*:?\s*//;
+
+ push @{$tracker->{features}}, {
+ id => $feature->{'bug_id'},
+ summary => $summary,
+ };
+ }
+}
+
+sub escape {
+ my $txt = shift;
+ $txt =~ s/&/&/g;
+ $txt =~ s/</g;
+ $txt =~ s/>/>/g;
+ return $txt;
+};
+
+print "\n";
+print "\n";
+print " \n";
+if (defined $title) {
+ print " ", &escape($title), "
\n";
+} else {
+ print " ", &escape($todosummary), "
\n";
+}
+if (defined $blurb) {
+ print " \n";
+ print $blurb;
+ print "
\n";
+}
+foreach my $tracker (sort { $a->{summary} cmp $b->{summary} } @trackers) {
+ next unless $#{$tracker->{features}} > 0;
+
+ my $summary = &escape($tracker->{summary});
+ my $id = $tracker->{id};
+ print " \n";
+ print " \n";
+ foreach my $feature (sort { $a->{summary} cmp $b->{summary} } @{$tracker->{features}}) {
+ $summary = &escape($feature->{summary});
+ $summary =~ s,^([^:]+):,$1,;
+
+ $id = $feature->{id};
+ print " - $summary (rhbz $id)
\n";
+ }
+ print "
\n";
+}
+
+print " \n";
+print " This page is automatically generated from ", &escape($todosummary), "\n";
+print "
\n";
+print " \n";
+print "\n";