← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stub/launchpad/pending-db-changes into lp:launchpad

 

Stuart Bishop has proposed merging lp:~stub/launchpad/pending-db-changes into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #689642 in Launchpad itself: "Database patches without a trailing semi-colon fail in replicated environment"
  https://bugs.launchpad.net/launchpad/+bug/689642
  Bug #726128 in Launchpad itself: "staging restore script creates invalid feature rule"
  https://bugs.launchpad.net/launchpad/+bug/726128

For more details, see:
https://code.launchpad.net/~stub/launchpad/pending-db-changes/+merge/54519

Renaming a primary key index breaks Slony-I, so don't do that.
-- 
https://code.launchpad.net/~stub/launchpad/pending-db-changes/+merge/54519
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stub/launchpad/pending-db-changes into lp:launchpad.
=== modified file 'configs/development/launchpad-lazr.conf'
--- configs/development/launchpad-lazr.conf	2011-03-18 01:17:42 +0000
+++ configs/development/launchpad-lazr.conf	2011-03-23 13:31:30 +0000
@@ -5,10 +5,6 @@
 [meta]
 extends: ../../lib/canonical/config/schema-lazr.conf
 
-[archivepublisher]
-root: /var/tmp/archive
-base_url: http://archive.launchpad.dev/
-
 [branchscanner]
 oops_prefix: BS
 error_dir: /var/tmp/codehosting.test

=== modified file 'configs/testrunner/launchpad-lazr.conf'
--- configs/testrunner/launchpad-lazr.conf	2011-02-25 19:27:05 +0000
+++ configs/testrunner/launchpad-lazr.conf	2011-03-23 13:31:30 +0000
@@ -8,9 +8,6 @@
 [canonical]
 cron_control_url: file:lib/lp/services/scripts/tests/cronscripts.ini
 
-[archivepublisher]
-base_url: http://ftpmaster.internal/
-
 [branchscanner]
 oops_prefix: TSMS
 error_dir: /var/tmp/lperr.test

=== modified file 'database/sampledata/current-dev.sql'
--- database/sampledata/current-dev.sql	2011-03-18 13:37:45 +0000
+++ database/sampledata/current-dev.sql	2011-03-23 13:31:30 +0000
@@ -837,6 +837,12 @@
 
 
 
+
+
+
+
+
+
 SET SESSION AUTHORIZATION DEFAULT;
 
 ALTER TABLE account DISABLE TRIGGER ALL;
@@ -1879,21 +1885,21 @@
 
 ALTER TABLE distroseries DISABLE TRIGGER ALL;
 
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (1, 1, 'warty', 'The Warty Warthog Release', 'Warty was the first stable release of Ubuntu. Key feature goals included releasing on time, with the latest version of the Gnome Desktop Environment, and the creation of all the infrastructure required to manage Ubuntu itself. Warty includes excellent support for Python, with most of the widely used Python libraries installed by default.', '4.10', 4, '2004-08-20 00:00:00', NULL, 17, 'Warty is the first release of Ubuntu, with a planned release date of October 2004.', 'Warty', NULL, 0, 1, 'warty-changes@xxxxxxxxxx', 4, 3, NULL, '2006-10-16 18:31:43.475428', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (2, 2, 'six', 'Six Six Six', 'some text to describe the whole 666 release of RH', '6.0.1', 4, '2004-03-21 00:00:00', NULL, 8, 'some text to describe the whole 666 release of RH', 'Six', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.482603', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (3, 1, 'hoary', 'The Hoary Hedgehog Release', 'Hoary is the second release of Ubuntu. Key feature goals include the integration of Hoary with the Launchpad for bugs and translation information, as well as Gnome 2.10 and the X.org window system.', '5.04', 2, NULL, 1, 1, 'Hoary is the second released of Ubuntu, with release planned for April 2005.', 'Hoary', NULL, 96, 6, 'hoary-changes@xxxxxxxxxx', 1, 4, NULL, '2006-10-16 18:31:43.483559', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (4, 2, '7.0', 'Seven', 'The release that we would not expect', '7.0.1', 3, NULL, 2, 7, 'The release that we would not expect', '7.0', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.484426', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (5, 1, 'grumpy', 'The Grumpy Groundhog Release', 'Grumpy, the third release of Ubuntu Linux, is not yet in active development. This information is purely a placeholder.', '5.10', 1, NULL, 1, 1, 'Grumpy is the third release of Ubuntu, planned for October 2005.', 'Grumpy', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.485233', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (6, 3, 'woody', 'WOODY', 'WOODY is the current stable verison of Debian GNU/Linux', '3.0', 4, '2003-01-01 00:00:00', NULL, 2, 'WOODY is the current stable verison of Debian GNU/Linux', 'Woody', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.486054', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (7, 3, 'sarge', 'Sarge', 'Sarge is the FROZEN unstable version of Debian GNU/Linux.', '3.1', 3, NULL, 6, 5, 'Sarge is the FROZEN unstable version of Debian GNU/Linux.', 'Sarge', NULL, 0, NULL, NULL, 0, 0, 6, '2006-10-16 18:31:43.486972', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (8, 3, 'sid', 'Sid', 'Sid is the CRAZY unstable version of Debian GNU/Linux.', '3.2', 1, NULL, 6, 6, 'Sid is the CRAZY unstable version of Debian GNU/Linux.', 'Sid', NULL, 0, NULL, NULL, 0, 1, NULL, '2006-10-16 18:31:43.487779', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (9, 7, '2k5', 'Guada 2005', 'This release places extra emphasis on usability and installability. The installer is adapted from Ubuntu to assume your country, language, keyboard and time zone preference, thus ensuring that installs ask the minimum number of questions possible.', '2005', 2, NULL, 3, 4, 'Guada 2005 is a rapid-install version of
-Ubuntu Hoary for the Andalucian marketplace.', 'Guada2005', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.488598', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (10, 1, 'breezy-autotest', 'Breezy Badger Autotest', 'Autotest version of Breezy', '6.6.6', 1, NULL, 3, 1, 'Autosync uploader test', 'Breezy Badger Autotest', NULL, 0, 8, 'autotest_changes@xxxxxxxxxx', 0, 0, NULL, '2006-10-16 18:31:43.489468', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (11, 8, 'breezy-autotest', 'Breezy Badger Autotest', 'Autotest version of Breezy', '6.6.6', 1, NULL, 1, 1, 'Autosync uploader test', 'Breezy Badger Autotest', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.490333', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (12, 5, 'krunch', 'The Krunchy Kangaroo', 'The archive split allows us to use different kernel settings for Kubuntu, as well as make other pervasive low-level fixes, and release on our own schedule.', '8.06', 1, NULL, 3, 1, 'This is the first experimental release of Kubuntu that uses a separate archive from the main Ubuntu release.', 'Krunch', NULL, 0, NULL, NULL, 0, 0, 33, '2006-10-16 18:31:43.491929', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (13, 8, 'hoary-test', 'Mock Hoary', 'nothing special', '9.9.9', 1, NULL, 1, 1, 'summmmmmmary', 'Hoary Mock', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.492845', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (14, 9, 'deriwarty', 'Deriwarty', 'Deriwarty', '1', 1, NULL, 1, 16, 'Deriwarty', 'Deriwarty', NULL, 0, NULL, NULL, 0, 0, NULL, '2011-03-17 14:29:23.190835', true, true, NULL, NULL, NULL, false);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (1, 1, 'warty', 'The Warty Warthog Release', 'Warty was the first stable release of Ubuntu. Key feature goals included releasing on time, with the latest version of the Gnome Desktop Environment, and the creation of all the infrastructure required to manage Ubuntu itself. Warty includes excellent support for Python, with most of the widely used Python libraries installed by default.', '4.10', 4, '2004-08-20 00:00:00', NULL, 'Warty is the first release of Ubuntu, with a planned release date of October 2004.', 'Warty', NULL, 0, 1, 'warty-changes@xxxxxxxxxx', 4, 3, NULL, '2006-10-16 18:31:43.475428', false, false, NULL, NULL, NULL, false, 17);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (2, 2, 'six', 'Six Six Six', 'some text to describe the whole 666 release of RH', '6.0.1', 4, '2004-03-21 00:00:00', NULL, 'some text to describe the whole 666 release of RH', 'Six', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.482603', false, false, NULL, NULL, NULL, false, 8);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (3, 1, 'hoary', 'The Hoary Hedgehog Release', 'Hoary is the second release of Ubuntu. Key feature goals include the integration of Hoary with the Launchpad for bugs and translation information, as well as Gnome 2.10 and the X.org window system.', '5.04', 2, NULL, 1, 'Hoary is the second released of Ubuntu, with release planned for April 2005.', 'Hoary', NULL, 96, 6, 'hoary-changes@xxxxxxxxxx', 1, 4, NULL, '2006-10-16 18:31:43.483559', false, false, NULL, NULL, NULL, false, 1);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (4, 2, '7.0', 'Seven', 'The release that we would not expect', '7.0.1', 3, NULL, 2, 'The release that we would not expect', '7.0', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.484426', false, false, NULL, NULL, NULL, false, 7);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (5, 1, 'grumpy', 'The Grumpy Groundhog Release', 'Grumpy, the third release of Ubuntu Linux, is not yet in active development. This information is purely a placeholder.', '5.10', 1, NULL, 1, 'Grumpy is the third release of Ubuntu, planned for October 2005.', 'Grumpy', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.485233', false, false, NULL, NULL, NULL, false, 1);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (6, 3, 'woody', 'WOODY', 'WOODY is the current stable verison of Debian GNU/Linux', '3.0', 4, '2003-01-01 00:00:00', NULL, 'WOODY is the current stable verison of Debian GNU/Linux', 'Woody', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.486054', false, false, NULL, NULL, NULL, false, 2);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (7, 3, 'sarge', 'Sarge', 'Sarge is the FROZEN unstable version of Debian GNU/Linux.', '3.1', 3, NULL, 6, 'Sarge is the FROZEN unstable version of Debian GNU/Linux.', 'Sarge', NULL, 0, NULL, NULL, 0, 0, 6, '2006-10-16 18:31:43.486972', false, false, NULL, NULL, NULL, false, 5);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (8, 3, 'sid', 'Sid', 'Sid is the CRAZY unstable version of Debian GNU/Linux.', '3.2', 1, NULL, 6, 'Sid is the CRAZY unstable version of Debian GNU/Linux.', 'Sid', NULL, 0, NULL, NULL, 0, 1, NULL, '2006-10-16 18:31:43.487779', false, false, NULL, NULL, NULL, false, 6);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (9, 7, '2k5', 'Guada 2005', 'This release places extra emphasis on usability and installability. The installer is adapted from Ubuntu to assume your country, language, keyboard and time zone preference, thus ensuring that installs ask the minimum number of questions possible.', '2005', 2, NULL, 3, 'Guada 2005 is a rapid-install version of
+Ubuntu Hoary for the Andalucian marketplace.', 'Guada2005', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.488598', false, false, NULL, NULL, NULL, false, 4);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (10, 1, 'breezy-autotest', 'Breezy Badger Autotest', 'Autotest version of Breezy', '6.6.6', 1, NULL, 3, 'Autosync uploader test', 'Breezy Badger Autotest', NULL, 0, 8, 'autotest_changes@xxxxxxxxxx', 0, 0, NULL, '2006-10-16 18:31:43.489468', false, false, NULL, NULL, NULL, false, 1);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (11, 8, 'breezy-autotest', 'Breezy Badger Autotest', 'Autotest version of Breezy', '6.6.6', 1, NULL, 1, 'Autosync uploader test', 'Breezy Badger Autotest', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.490333', false, false, NULL, NULL, NULL, false, 1);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (12, 5, 'krunch', 'The Krunchy Kangaroo', 'The archive split allows us to use different kernel settings for Kubuntu, as well as make other pervasive low-level fixes, and release on our own schedule.', '8.06', 1, NULL, 3, 'This is the first experimental release of Kubuntu that uses a separate archive from the main Ubuntu release.', 'Krunch', NULL, 0, NULL, NULL, 0, 0, 33, '2006-10-16 18:31:43.491929', false, false, NULL, NULL, NULL, false, 1);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (13, 8, 'hoary-test', 'Mock Hoary', 'nothing special', '9.9.9', 1, NULL, 1, 'summmmmmmary', 'Hoary Mock', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.492845', false, false, NULL, NULL, NULL, false, 1);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (14, 9, 'deriwarty', 'Deriwarty', 'Deriwarty', '1', 1, NULL, 1, 'Deriwarty', 'Deriwarty', NULL, 0, NULL, NULL, 0, 0, NULL, '2011-03-17 14:29:23.190835', true, true, NULL, NULL, NULL, false, 16);
 
 
 ALTER TABLE distroseries ENABLE TRIGGER ALL;
@@ -1909,22 +1915,22 @@
 
 ALTER TABLE distribution DISABLE TRIGGER ALL;
 
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (1, 'ubuntu', 'Ubuntu Linux', 'Ubuntu is a new approach to Linux Distribution that includes regular releases, and a simplified single-CD installation system.', 'ubuntulinux.org', 17, 'Ubuntu', 'Ubuntu is a new approach to Linux Distribution that includes regular releases, and a simplified single-CD installation system.', 17, NULL, 1, NULL, true, true, NULL, NULL, 3, 59, NULL, NULL, '2006-10-16 18:31:43.415195', NULL, NULL, NULL, NULL, NULL, true, NULL, true, true, NULL, NULL, NULL, NULL, 20, 20, 20);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (2, 'redhat', 'Redhat Advanced Server', 'Red Hat is a commercial distribution of the GNU/Linux Operating System.', 'redhat.com', 1, 'Red Hat', 'Red Hat is a commercial distribution of the GNU/Linux Operating System.', 1, NULL, 1, NULL, false, false, NULL, 8, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.417928', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (3, 'debian', 'Debian GNU/Linux', 'Debian GNU/Linux is
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (1, 'ubuntu', 'Ubuntu Linux', 'Ubuntu is a new approach to Linux Distribution that includes regular releases, and a simplified single-CD installation system.', 'ubuntulinux.org', 17, 'Ubuntu', 'Ubuntu is a new approach to Linux Distribution that includes regular releases, and a simplified single-CD installation system.', 17, NULL, 1, NULL, true, true, NULL, NULL, 3, 59, NULL, NULL, '2006-10-16 18:31:43.415195', NULL, NULL, NULL, NULL, NULL, true, NULL, true, true, NULL, NULL, NULL, NULL, 20, 20, 20, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (2, 'redhat', 'Redhat Advanced Server', 'Red Hat is a commercial distribution of the GNU/Linux Operating System.', 'redhat.com', 1, 'Red Hat', 'Red Hat is a commercial distribution of the GNU/Linux Operating System.', 1, NULL, 1, NULL, false, false, NULL, 8, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.417928', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (3, 'debian', 'Debian GNU/Linux', 'Debian GNU/Linux is
 a non commercial distribution of a GNU/Linux Operating System for many
 platforms.', 'debian.org', 1, 'Debian', 'Debian GNU/Linux is
 a non commercial distribution of a GNU/Linux Operating System for many
-platforms.', 1, NULL, 1, NULL, false, false, NULL, NULL, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.418942', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (4, 'gentoo', 'The Gentoo Linux', 'Gentoo is a very
+platforms.', 1, NULL, 1, NULL, false, false, NULL, NULL, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.418942', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (4, 'gentoo', 'The Gentoo Linux', 'Gentoo is a very
 customizeable GNU/Linux Distribution that is designed to let you build every
-single package yourself, with your own preferences.', 'gentoo.org', 1, 'Gentoo', 'Gentoo is a very customizeable GNU/Linux Distribution that is designed to let you build every single package yourself, with your own preferences.', 1, NULL, 1, NULL, true, false, NULL, NULL, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.41974', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (5, 'kubuntu', 'Kubuntu - Free KDE-based Linux', 'Kubuntu is an entirely free Linux distribution that uses the K Desktop
+single package yourself, with your own preferences.', 'gentoo.org', 1, 'Gentoo', 'Gentoo is a very customizeable GNU/Linux Distribution that is designed to let you build every single package yourself, with your own preferences.', 1, NULL, 1, NULL, true, false, NULL, NULL, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.41974', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (5, 'kubuntu', 'Kubuntu - Free KDE-based Linux', 'Kubuntu is an entirely free Linux distribution that uses the K Desktop
 Environment as its default desktop after install.', 'kubuntu.org', 1, 'Kubuntu', 'Kubuntu is an entirely free Linux distribution that uses the K Desktop
-Environment as its default desktop after install.', 1, NULL, 1, NULL, false, false, NULL, 8, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.420551', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (7, 'guadalinex', 'GuadaLinex: Linux for Andalucia', 'GuadaLinex is based on Ubuntu and adds full support for applications specific to the local environment in Andalucia.', 'guadalinex.es', 4, 'GuadaLinex', 'The GuadaLinex team produces a high quality linux for the Andalucian marketplace.', 32, NULL, 1, NULL, false, false, NULL, NULL, NULL, 4, NULL, NULL, '2006-10-16 18:31:43.421329', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (8, 'ubuntutest', 'Ubuntu Test', 'Ubuntu Test', 'ubuntulinux.org', 17, 'ubuntutest', 'Ubuntu Test summary', 17, NULL, 1, NULL, false, false, NULL, NULL, NULL, 17, NULL, NULL, '2006-10-16 18:31:43.422162', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (9, 'deribuntu', 'Deribuntu', 'Deribuntu', 'deribuntu', 16, 'Deribuntu', 'Deribuntu', 16, NULL, 1, NULL, false, false, NULL, NULL, NULL, 16, NULL, NULL, '2011-03-17 14:28:54.354337', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
+Environment as its default desktop after install.', 1, NULL, 1, NULL, false, false, NULL, 8, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.420551', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (7, 'guadalinex', 'GuadaLinex: Linux for Andalucia', 'GuadaLinex is based on Ubuntu and adds full support for applications specific to the local environment in Andalucia.', 'guadalinex.es', 4, 'GuadaLinex', 'The GuadaLinex team produces a high quality linux for the Andalucian marketplace.', 32, NULL, 1, NULL, false, false, NULL, NULL, NULL, 4, NULL, NULL, '2006-10-16 18:31:43.421329', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (8, 'ubuntutest', 'Ubuntu Test', 'Ubuntu Test', 'ubuntulinux.org', 17, 'ubuntutest', 'Ubuntu Test summary', 17, NULL, 1, NULL, false, false, NULL, NULL, NULL, 17, NULL, NULL, '2006-10-16 18:31:43.422162', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (9, 'deribuntu', 'Deribuntu', 'Deribuntu', 'deribuntu', 16, 'Deribuntu', 'Deribuntu', 16, NULL, 1, NULL, false, false, NULL, NULL, NULL, 16, NULL, NULL, '2011-03-17 14:28:54.354337', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
 
 
 ALTER TABLE distribution ENABLE TRIGGER ALL;
@@ -2944,57 +2950,57 @@
 
 ALTER TABLE message DISABLE TRIGGER ALL;
 
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (1, '2004-09-24 20:58:04.684057', 'PEBCAK', 16, NULL, NULL, 'foo@xxxxxxxxxxx-332342--1231', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (3, '2004-09-24 21:17:17.153792', 'Reproduced on AIX', 12, NULL, NULL, 'sdsdfsfd', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (4, '2004-09-24 21:24:03.922564', 'Re: Reproduced on AIX', 12, NULL, NULL, 'sdfssfdfsd', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (5, '2004-09-24 21:29:27.407354', 'Fantastic idea, I''d really like to see this', 12, NULL, NULL, 'dxssdfsdgf', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (6, '2004-09-24 21:35:20.125564', 'Strange bug with duplicate messages.', 12, NULL, NULL, 'sdfsfwew', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (7, '2005-01-14 17:20:12.820778', 'Reflow problems with complex page layouts', 12, NULL, NULL, '<20050114172012.6687.51124.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (8, '2005-01-14 17:27:03.702622', 'Firefox install instructions should be complete', 12, NULL, NULL, '<20050114172703.6687.71983.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (9, '2005-01-14 17:35:39.548665', 'Firefox crashes when Save As dialog for a nonexistent window is closed', 12, NULL, NULL, '<20050114173539.6687.81610.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (10, '2004-10-05 00:00:00', 'Re: Bug Title Test', 12, NULL, NULL, '<20050831114528.7616.78129.malone@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (11, '2005-10-14 15:12:29.602117', 'A test bug', 16, NULL, NULL, '<20051014151229.28962.1536.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (12, '2005-10-14 12:25:21.508923', 'Re: Newly installed plug-in doesn''t seem to be used', 16, NULL, NULL, '<20051014122521.14276.39260.lptickets@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (13, '2005-10-14 13:28:11.554476', 'Re: Slow system', 12, NULL, NULL, '<20051014132811.14276.65873.lptickets@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (14, '2005-10-28 09:10:17.13237', 'Printing doesn''t work', 12, NULL, 3, '<20051028091017.6690.9505.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (15, '2006-02-23 16:42:14.080227', 'Thunderbird crashes', 16, NULL, 1, '<20060223164214.9126.7558.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (16, '2006-06-16 17:12:54', 'Unicodeâ„¢', 16, NULL, NULL, '<20060616141252.22134.71562@localhost.localdomain>', NULL, 51);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (17, '2006-02-22 19:42:21.890299', 'another test bug', 16, NULL, 1, '<20060222194221.25842.69665.malonedeb@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (18, '2006-07-20 20:48:24.975495', 'Re: Continue playing after shutdown', 16, NULL, NULL, '<20060720204825.13277.37433.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (19, '2006-07-20 20:49:47.551344', 'Re: mailto: problem in webpage', 16, NULL, NULL, '<20060720204947.13277.79684.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (20, '2006-07-20 20:52:07.054216', 'Re: Installation of Java Runtime Environment for Mozilla', 16, NULL, NULL, '<20060720205207.13277.68582.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (21, '2006-07-20 20:53:53.684848', 'Re: Play DVDs in Totem', 16, NULL, NULL, '<20060720205354.13277.37000.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (22, '2006-07-20 20:56:35.442839', 'Re: mailto: problem in webpage', 12, NULL, NULL, '<20060720205635.13277.87295.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (23, '2006-07-20 23:11:24.975495', 'Re: Continue playing after shutdown', 12, NULL, NULL, '<20061201222020.597.97888.lptickets@xxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (24, '2007-03-15 20:33:56.67893', 'Make Jokosher use autoaudiosink', 26, NULL, NULL, '<20070315203356.12919.76581.malonedeb@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (25, '2007-03-15 20:34:26.518114', 'Re: Make Jokosher use autoaudiosink', 50, NULL, NULL, '<20070315203426.12919.54628.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (26, '2007-03-15 20:35:10.133383', 'Re: Make Jokosher use autoaudiosink', 66, NULL, NULL, '<20070315203510.12919.22697.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (27, '2007-03-15 20:36:01.779544', 'Autoaudiosink is no longer under development', 63, NULL, NULL, '<20070315203601.12919.29640.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (28, '2007-03-15 20:36:57.133832', 'Re: Autoaudiosink is no longer under development', 27, NULL, NULL, '<20070315203657.12919.48585.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (29, '2007-03-15 20:37:27.991571', 'This is a really new title', 33, NULL, NULL, '<20070315203728.12919.76787.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (30, '2007-03-15 20:37:51.544376', 'Re: Make Jokosher use autoaudiosink', 3, NULL, NULL, '<20070315203751.12919.49072.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (31, '2007-03-15 20:41:18.635493', 'Copy, Cut and Delete operations should work on selections', 8, NULL, NULL, '<20070315204118.14326.61124.malonedeb@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (32, '2007-03-15 20:41:42.154264', 'Re: Copy, Cut and Delete operations should work on selections', 16, NULL, NULL, '<20070315204142.14326.82988.launchpad@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (33, '2007-03-15 20:43:14.981111', 'Re: Copy, Cut and Delete operations should work on selections', 45, NULL, NULL, '<20070315204315.14326.75272.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (34, '2007-03-15 20:45:15.852052', 'Re: Copy, Cut and Delete operations should work on selections', 13, NULL, NULL, '<20070315204515.14326.38817.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (35, '2007-03-15 20:45:51.817826', 'Re: Copy, Cut and Delete operations should work on selections', 9, NULL, NULL, '<20070315204551.14326.36994.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (36, '2007-03-15 20:46:49.83307', 'Re: Copy, Cut and Delete operations should work on selections', 6, NULL, NULL, '<20070315204649.14326.69581.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (37, '2007-07-27 20:00:58.299796', 'Launchpad CSS and JS is not testible', 12, NULL, NULL, '<20070727200058.25131.76173.malonedeb@autumn.annrky-sinzui.local>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (38, '2007-07-27 20:29:46.25854', 'Re: Launchpad CSS and JS is not testible', 12, NULL, NULL, '<20070727202946.25131.16206.malone@autumn.annrky-sinzui.local>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (39, '2007-08-09 11:39:16.836856', 'jokosher exposes personal details in its actions portlet', 63, NULL, NULL, '<20070809113916.26819.83859.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (40, '2004-12-18 16:30:19.103679', 'Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163019.18924.87555.malonedeb@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (41, '2007-12-18 16:30:47.889614', 'Re: Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163048.18924.13348.launchpad@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (42, '2007-12-18 16:30:47.889614', 'Re: Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163048.18924.86681.launchpad@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (43, '2007-12-18 16:31:34.790641', 'Re: Nonsensical bugs are useless', 62, NULL, NULL, '<20071218163134.18996.53651.launchpad@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (44, '2005-05-13 16:37:37', 'gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, NULL, NULL, '<4284D7D1.6010208@xxxxxx>', NULL, 75);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (45, '2005-05-17 18:54:29', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243615, 44, NULL, '<20050517185429.GB20786@xxxxxxxxxxxxxxx>', NULL, 76);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (46, '2005-05-17 19:24:25', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, 45, NULL, '<428A44E9.6090802@xxxxxx>', NULL, 77);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (47, '2005-05-17 20:20:44', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243615, 46, NULL, '<20050517202044.GA23231@xxxxxxxxxxxxxxx>', NULL, 78);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (48, '2005-06-17 14:00:11', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243616, 46, NULL, '<20050617140011.GA15638@xxxxxxxxx>', NULL, 79);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (49, '2005-06-25 10:13:10', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, NULL, NULL, '<42BD2E36.9090809@xxxxxx>', NULL, 81);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (50, '2010-06-25 10:13:10', 'Sample comment 1', 1, NULL, NULL, '<42BD2E36.9090810@xxxxx>', NULL, 81);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (51, '2010-06-26 10:13:10', 'Sample comment 2', 1, NULL, NULL, '<42BD2E36.9090810@xxxxx>', NULL, 81);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (52, '2010-06-27 10:13:10', 'Sample comment 3', 1, NULL, NULL, '<42BD2E36.9090810@xxxxx>', NULL, 81);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (1, '2004-09-24 20:58:04.684057', 'PEBCAK', 16, NULL, NULL, 'foo@xxxxxxxxxxx-332342--1231', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (3, '2004-09-24 21:17:17.153792', 'Reproduced on AIX', 12, NULL, NULL, 'sdsdfsfd', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (4, '2004-09-24 21:24:03.922564', 'Re: Reproduced on AIX', 12, NULL, NULL, 'sdfssfdfsd', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (5, '2004-09-24 21:29:27.407354', 'Fantastic idea, I''d really like to see this', 12, NULL, NULL, 'dxssdfsdgf', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (6, '2004-09-24 21:35:20.125564', 'Strange bug with duplicate messages.', 12, NULL, NULL, 'sdfsfwew', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (7, '2005-01-14 17:20:12.820778', 'Reflow problems with complex page layouts', 12, NULL, NULL, '<20050114172012.6687.51124.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (8, '2005-01-14 17:27:03.702622', 'Firefox install instructions should be complete', 12, NULL, NULL, '<20050114172703.6687.71983.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (9, '2005-01-14 17:35:39.548665', 'Firefox crashes when Save As dialog for a nonexistent window is closed', 12, NULL, NULL, '<20050114173539.6687.81610.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (10, '2004-10-05 00:00:00', 'Re: Bug Title Test', 12, NULL, NULL, '<20050831114528.7616.78129.malone@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (11, '2005-10-14 15:12:29.602117', 'A test bug', 16, NULL, NULL, '<20051014151229.28962.1536.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (12, '2005-10-14 12:25:21.508923', 'Re: Newly installed plug-in doesn''t seem to be used', 16, NULL, NULL, '<20051014122521.14276.39260.lptickets@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (13, '2005-10-14 13:28:11.554476', 'Re: Slow system', 12, NULL, NULL, '<20051014132811.14276.65873.lptickets@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (14, '2005-10-28 09:10:17.13237', 'Printing doesn''t work', 12, NULL, 3, '<20051028091017.6690.9505.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (15, '2006-02-23 16:42:14.080227', 'Thunderbird crashes', 16, NULL, 1, '<20060223164214.9126.7558.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (16, '2006-06-16 17:12:54', 'Unicodeâ„¢', 16, NULL, NULL, '<20060616141252.22134.71562@localhost.localdomain>', NULL, 51, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (17, '2006-02-22 19:42:21.890299', 'another test bug', 16, NULL, 1, '<20060222194221.25842.69665.malonedeb@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (18, '2006-07-20 20:48:24.975495', 'Re: Continue playing after shutdown', 16, NULL, NULL, '<20060720204825.13277.37433.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (19, '2006-07-20 20:49:47.551344', 'Re: mailto: problem in webpage', 16, NULL, NULL, '<20060720204947.13277.79684.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (20, '2006-07-20 20:52:07.054216', 'Re: Installation of Java Runtime Environment for Mozilla', 16, NULL, NULL, '<20060720205207.13277.68582.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (21, '2006-07-20 20:53:53.684848', 'Re: Play DVDs in Totem', 16, NULL, NULL, '<20060720205354.13277.37000.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (22, '2006-07-20 20:56:35.442839', 'Re: mailto: problem in webpage', 12, NULL, NULL, '<20060720205635.13277.87295.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (23, '2006-07-20 23:11:24.975495', 'Re: Continue playing after shutdown', 12, NULL, NULL, '<20061201222020.597.97888.lptickets@xxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (24, '2007-03-15 20:33:56.67893', 'Make Jokosher use autoaudiosink', 26, NULL, NULL, '<20070315203356.12919.76581.malonedeb@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (25, '2007-03-15 20:34:26.518114', 'Re: Make Jokosher use autoaudiosink', 50, NULL, NULL, '<20070315203426.12919.54628.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (26, '2007-03-15 20:35:10.133383', 'Re: Make Jokosher use autoaudiosink', 66, NULL, NULL, '<20070315203510.12919.22697.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (27, '2007-03-15 20:36:01.779544', 'Autoaudiosink is no longer under development', 63, NULL, NULL, '<20070315203601.12919.29640.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (28, '2007-03-15 20:36:57.133832', 'Re: Autoaudiosink is no longer under development', 27, NULL, NULL, '<20070315203657.12919.48585.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (29, '2007-03-15 20:37:27.991571', 'This is a really new title', 33, NULL, NULL, '<20070315203728.12919.76787.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (30, '2007-03-15 20:37:51.544376', 'Re: Make Jokosher use autoaudiosink', 3, NULL, NULL, '<20070315203751.12919.49072.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (31, '2007-03-15 20:41:18.635493', 'Copy, Cut and Delete operations should work on selections', 8, NULL, NULL, '<20070315204118.14326.61124.malonedeb@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (32, '2007-03-15 20:41:42.154264', 'Re: Copy, Cut and Delete operations should work on selections', 16, NULL, NULL, '<20070315204142.14326.82988.launchpad@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (33, '2007-03-15 20:43:14.981111', 'Re: Copy, Cut and Delete operations should work on selections', 45, NULL, NULL, '<20070315204315.14326.75272.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (34, '2007-03-15 20:45:15.852052', 'Re: Copy, Cut and Delete operations should work on selections', 13, NULL, NULL, '<20070315204515.14326.38817.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (35, '2007-03-15 20:45:51.817826', 'Re: Copy, Cut and Delete operations should work on selections', 9, NULL, NULL, '<20070315204551.14326.36994.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (36, '2007-03-15 20:46:49.83307', 'Re: Copy, Cut and Delete operations should work on selections', 6, NULL, NULL, '<20070315204649.14326.69581.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (37, '2007-07-27 20:00:58.299796', 'Launchpad CSS and JS is not testible', 12, NULL, NULL, '<20070727200058.25131.76173.malonedeb@autumn.annrky-sinzui.local>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (38, '2007-07-27 20:29:46.25854', 'Re: Launchpad CSS and JS is not testible', 12, NULL, NULL, '<20070727202946.25131.16206.malone@autumn.annrky-sinzui.local>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (39, '2007-08-09 11:39:16.836856', 'jokosher exposes personal details in its actions portlet', 63, NULL, NULL, '<20070809113916.26819.83859.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (40, '2004-12-18 16:30:19.103679', 'Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163019.18924.87555.malonedeb@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (41, '2007-12-18 16:30:47.889614', 'Re: Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163048.18924.13348.launchpad@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (42, '2007-12-18 16:30:47.889614', 'Re: Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163048.18924.86681.launchpad@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (43, '2007-12-18 16:31:34.790641', 'Re: Nonsensical bugs are useless', 62, NULL, NULL, '<20071218163134.18996.53651.launchpad@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (44, '2005-05-13 16:37:37', 'gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, NULL, NULL, '<4284D7D1.6010208@xxxxxx>', NULL, 75, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (45, '2005-05-17 18:54:29', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243615, 44, NULL, '<20050517185429.GB20786@xxxxxxxxxxxxxxx>', NULL, 76, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (46, '2005-05-17 19:24:25', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, 45, NULL, '<428A44E9.6090802@xxxxxx>', NULL, 77, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (47, '2005-05-17 20:20:44', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243615, 46, NULL, '<20050517202044.GA23231@xxxxxxxxxxxxxxx>', NULL, 78, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (48, '2005-06-17 14:00:11', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243616, 46, NULL, '<20050617140011.GA15638@xxxxxxxxx>', NULL, 79, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (49, '2005-06-25 10:13:10', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, NULL, NULL, '<42BD2E36.9090809@xxxxxx>', NULL, 81, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (50, '2010-06-25 10:13:10', 'Sample comment 1', 1, NULL, NULL, '<42BD2E36.9090810@xxxxx>', NULL, 81, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (51, '2010-06-26 10:13:10', 'Sample comment 2', 1, NULL, NULL, '<42BD2E36.9090810@xxxxx>', NULL, 81, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (52, '2010-06-27 10:13:10', 'Sample comment 3', 1, NULL, NULL, '<42BD2E36.9090810@xxxxx>', NULL, 81, true);
 
 
 ALTER TABLE message ENABLE TRIGGER ALL;
@@ -3392,42 +3398,42 @@
 
 ALTER TABLE bugmessage DISABLE TRIGGER ALL;
 
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (1, 2, 1, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (2, 1, 3, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (3, 1, 4, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (4, 2, 5, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (5, 2, 6, NULL, NULL, true, 2);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (6, 4, 7, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (7, 5, 8, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (8, 6, 9, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (9, 3, 10, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (10, 7, 11, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (11, 8, 14, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (12, 9, 15, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (13, 10, 17, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (14, 10, 16, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (15, 11, 24, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (16, 11, 25, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (17, 11, 26, NULL, NULL, true, 2);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (18, 11, 27, NULL, NULL, true, 3);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (19, 11, 28, NULL, NULL, true, 4);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (20, 11, 29, NULL, NULL, true, 5);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (21, 11, 30, NULL, NULL, true, 6);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (22, 12, 31, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (23, 12, 33, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (24, 12, 34, NULL, NULL, true, 2);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (25, 12, 35, NULL, NULL, true, 3);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (26, 12, 36, NULL, NULL, true, 4);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (27, 13, 37, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (28, 13, 38, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (29, 14, 39, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (30, 15, 40, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (31, 15, 44, 11, '<4284D7D1.6010208@xxxxxx>', true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (32, 15, 45, 11, '<20050517185429.GB20786@xxxxxxxxxxxxxxx>', true, 2);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (33, 15, 46, 11, '<428A44E9.6090802@xxxxxx>', true, 3);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (34, 15, 47, 11, '<20050517202044.GA23231@xxxxxxxxxxxxxxx>', true, 4);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (35, 15, 48, 11, '<20050617140011.GA15638@xxxxxxxxx>', true, 5);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (36, 15, 49, 11, '<42BD2E36.9090809@xxxxxx>', true, 6);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (1, 2, 1, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (2, 1, 3, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (3, 1, 4, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (4, 2, 5, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (5, 2, 6, NULL, NULL, 2);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (6, 4, 7, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (7, 5, 8, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (8, 6, 9, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (9, 3, 10, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (10, 7, 11, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (11, 8, 14, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (12, 9, 15, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (13, 10, 17, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (14, 10, 16, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (15, 11, 24, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (16, 11, 25, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (17, 11, 26, NULL, NULL, 2);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (18, 11, 27, NULL, NULL, 3);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (19, 11, 28, NULL, NULL, 4);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (20, 11, 29, NULL, NULL, 5);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (21, 11, 30, NULL, NULL, 6);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (22, 12, 31, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (23, 12, 33, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (24, 12, 34, NULL, NULL, 2);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (25, 12, 35, NULL, NULL, 3);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (26, 12, 36, NULL, NULL, 4);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (27, 13, 37, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (28, 13, 38, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (29, 14, 39, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (30, 15, 40, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (31, 15, 44, 11, '<4284D7D1.6010208@xxxxxx>', 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (32, 15, 45, 11, '<20050517185429.GB20786@xxxxxxxxxxxxxxx>', 2);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (33, 15, 46, 11, '<428A44E9.6090802@xxxxxx>', 3);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (34, 15, 47, 11, '<20050517202044.GA23231@xxxxxxxxxxxxxxx>', 4);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (35, 15, 48, 11, '<20050617140011.GA15638@xxxxxxxxx>', 5);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (36, 15, 49, 11, '<42BD2E36.9090809@xxxxxx>', 6);
 
 
 ALTER TABLE bugmessage ENABLE TRIGGER ALL;
@@ -4051,6 +4057,13 @@
 ALTER TABLE databasecpustats ENABLE TRIGGER ALL;
 
 
+ALTER TABLE databasediskutilization DISABLE TRIGGER ALL;
+
+
+
+ALTER TABLE databasediskutilization ENABLE TRIGGER ALL;
+
+
 ALTER TABLE databasereplicationlag DISABLE TRIGGER ALL;
 
 
@@ -4447,6 +4460,20 @@
 ALTER TABLE featureflag ENABLE TRIGGER ALL;
 
 
+ALTER TABLE featureflagchangelogentry DISABLE TRIGGER ALL;
+
+
+
+ALTER TABLE featureflagchangelogentry ENABLE TRIGGER ALL;
+
+
+ALTER TABLE featureflagchangelogentry DISABLE TRIGGER ALL;
+
+
+
+ALTER TABLE featureflagchangelogentry ENABLE TRIGGER ALL;
+
+
 ALTER TABLE flatpackagesetinclusion DISABLE TRIGGER ALL;
 
 
@@ -9961,6 +9988,15 @@
 ALTER TABLE projectrelationship ENABLE TRIGGER ALL;
 
 
+ALTER TABLE publisherconfig DISABLE TRIGGER ALL;
+
+INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (1, 1, '/var/tmp/archive', 'http://archive.launchpad.dev/', 'http://rebuild-test.internal/');
+INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (2, 8, '/var/tmp/archive', 'http://archive.launchpad.dev/', 'http://rebuild-test.internal/');
+
+
+ALTER TABLE publisherconfig ENABLE TRIGGER ALL;
+
+
 ALTER TABLE pushmirroraccess DISABLE TRIGGER ALL;
 
 

=== modified file 'database/sampledata/current.sql'
--- database/sampledata/current.sql	2011-02-25 17:46:17 +0000
+++ database/sampledata/current.sql	2011-03-23 13:31:30 +0000
@@ -837,6 +837,12 @@
 
 
 
+
+
+
+
+
+
 SET SESSION AUTHORIZATION DEFAULT;
 
 ALTER TABLE account DISABLE TRIGGER ALL;
@@ -1879,20 +1885,20 @@
 
 ALTER TABLE distroseries DISABLE TRIGGER ALL;
 
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (1, 1, 'warty', 'The Warty Warthog Release', 'Warty was the first stable release of Ubuntu. Key feature goals included releasing on time, with the latest version of the Gnome Desktop Environment, and the creation of all the infrastructure required to manage Ubuntu itself. Warty includes excellent support for Python, with most of the widely used Python libraries installed by default.', '4.10', 4, '2004-08-20 00:00:00', NULL, 17, 'Warty is the first release of Ubuntu, with a planned release date of October 2004.', 'Warty', NULL, 0, 1, 'warty-changes@xxxxxxxxxx', 4, 3, NULL, '2006-10-16 18:31:43.475428', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (2, 2, 'six', 'Six Six Six', 'some text to describe the whole 666 release of RH', '6.0.1', 4, '2004-03-21 00:00:00', NULL, 8, 'some text to describe the whole 666 release of RH', 'Six', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.482603', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (3, 1, 'hoary', 'The Hoary Hedgehog Release', 'Hoary is the second release of Ubuntu. Key feature goals include the integration of Hoary with the Launchpad for bugs and translation information, as well as Gnome 2.10 and the X.org window system.', '5.04', 2, NULL, 1, 1, 'Hoary is the second released of Ubuntu, with release planned for April 2005.', 'Hoary', NULL, 96, 6, 'hoary-changes@xxxxxxxxxx', 1, 4, NULL, '2006-10-16 18:31:43.483559', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (4, 2, '7.0', 'Seven', 'The release that we would not expect', '7.0.1', 3, NULL, 2, 7, 'The release that we would not expect', '7.0', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.484426', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (5, 1, 'grumpy', 'The Grumpy Groundhog Release', 'Grumpy, the third release of Ubuntu Linux, is not yet in active development. This information is purely a placeholder.', '5.10', 1, NULL, 1, 1, 'Grumpy is the third release of Ubuntu, planned for October 2005.', 'Grumpy', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.485233', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (6, 3, 'woody', 'WOODY', 'WOODY is the current stable verison of Debian GNU/Linux', '3.0', 4, '2003-01-01 00:00:00', NULL, 2, 'WOODY is the current stable verison of Debian GNU/Linux', 'Woody', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.486054', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (7, 3, 'sarge', 'Sarge', 'Sarge is the FROZEN unstable version of Debian GNU/Linux.', '3.1', 3, NULL, 6, 5, 'Sarge is the FROZEN unstable version of Debian GNU/Linux.', 'Sarge', NULL, 0, NULL, NULL, 0, 0, 6, '2006-10-16 18:31:43.486972', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (8, 3, 'sid', 'Sid', 'Sid is the CRAZY unstable version of Debian GNU/Linux.', '3.2', 1, NULL, 6, 6, 'Sid is the CRAZY unstable version of Debian GNU/Linux.', 'Sid', NULL, 0, NULL, NULL, 0, 1, NULL, '2006-10-16 18:31:43.487779', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (9, 7, '2k5', 'Guada 2005', 'This release places extra emphasis on usability and installability. The installer is adapted from Ubuntu to assume your country, language, keyboard and time zone preference, thus ensuring that installs ask the minimum number of questions possible.', '2005', 2, NULL, 3, 4, 'Guada 2005 is a rapid-install version of
-Ubuntu Hoary for the Andalucian marketplace.', 'Guada2005', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.488598', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (10, 1, 'breezy-autotest', 'Breezy Badger Autotest', 'Autotest version of Breezy', '6.6.6', 1, NULL, 3, 1, 'Autosync uploader test', 'Breezy Badger Autotest', NULL, 0, 8, 'autotest_changes@xxxxxxxxxx', 0, 0, NULL, '2006-10-16 18:31:43.489468', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (11, 8, 'breezy-autotest', 'Breezy Badger Autotest', 'Autotest version of Breezy', '6.6.6', 1, NULL, 1, 1, 'Autosync uploader test', 'Breezy Badger Autotest', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.490333', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (12, 5, 'krunch', 'The Krunchy Kangaroo', 'The archive split allows us to use different kernel settings for Kubuntu, as well as make other pervasive low-level fixes, and release on our own schedule.', '8.06', 1, NULL, 3, 1, 'This is the first experimental release of Kubuntu that uses a separate archive from the main Ubuntu release.', 'Krunch', NULL, 0, NULL, NULL, 0, 0, 33, '2006-10-16 18:31:43.491929', false, false, NULL, NULL, NULL, false);
-INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, owner, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested) VALUES (13, 8, 'hoary-test', 'Mock Hoary', 'nothing special', '9.9.9', 1, NULL, 1, 1, 'summmmmmmary', 'Hoary Mock', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.492845', false, false, NULL, NULL, NULL, false);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (1, 1, 'warty', 'The Warty Warthog Release', 'Warty was the first stable release of Ubuntu. Key feature goals included releasing on time, with the latest version of the Gnome Desktop Environment, and the creation of all the infrastructure required to manage Ubuntu itself. Warty includes excellent support for Python, with most of the widely used Python libraries installed by default.', '4.10', 4, '2004-08-20 00:00:00', NULL, 'Warty is the first release of Ubuntu, with a planned release date of October 2004.', 'Warty', NULL, 0, 1, 'warty-changes@xxxxxxxxxx', 4, 3, NULL, '2006-10-16 18:31:43.475428', false, false, NULL, NULL, NULL, false, 17);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (2, 2, 'six', 'Six Six Six', 'some text to describe the whole 666 release of RH', '6.0.1', 4, '2004-03-21 00:00:00', NULL, 'some text to describe the whole 666 release of RH', 'Six', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.482603', false, false, NULL, NULL, NULL, false, 8);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (3, 1, 'hoary', 'The Hoary Hedgehog Release', 'Hoary is the second release of Ubuntu. Key feature goals include the integration of Hoary with the Launchpad for bugs and translation information, as well as Gnome 2.10 and the X.org window system.', '5.04', 2, NULL, 1, 'Hoary is the second released of Ubuntu, with release planned for April 2005.', 'Hoary', NULL, 96, 6, 'hoary-changes@xxxxxxxxxx', 1, 4, NULL, '2006-10-16 18:31:43.483559', false, false, NULL, NULL, NULL, false, 1);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (4, 2, '7.0', 'Seven', 'The release that we would not expect', '7.0.1', 3, NULL, 2, 'The release that we would not expect', '7.0', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.484426', false, false, NULL, NULL, NULL, false, 7);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (5, 1, 'grumpy', 'The Grumpy Groundhog Release', 'Grumpy, the third release of Ubuntu Linux, is not yet in active development. This information is purely a placeholder.', '5.10', 1, NULL, 1, 'Grumpy is the third release of Ubuntu, planned for October 2005.', 'Grumpy', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.485233', false, false, NULL, NULL, NULL, false, 1);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (6, 3, 'woody', 'WOODY', 'WOODY is the current stable verison of Debian GNU/Linux', '3.0', 4, '2003-01-01 00:00:00', NULL, 'WOODY is the current stable verison of Debian GNU/Linux', 'Woody', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.486054', false, false, NULL, NULL, NULL, false, 2);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (7, 3, 'sarge', 'Sarge', 'Sarge is the FROZEN unstable version of Debian GNU/Linux.', '3.1', 3, NULL, 6, 'Sarge is the FROZEN unstable version of Debian GNU/Linux.', 'Sarge', NULL, 0, NULL, NULL, 0, 0, 6, '2006-10-16 18:31:43.486972', false, false, NULL, NULL, NULL, false, 5);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (8, 3, 'sid', 'Sid', 'Sid is the CRAZY unstable version of Debian GNU/Linux.', '3.2', 1, NULL, 6, 'Sid is the CRAZY unstable version of Debian GNU/Linux.', 'Sid', NULL, 0, NULL, NULL, 0, 1, NULL, '2006-10-16 18:31:43.487779', false, false, NULL, NULL, NULL, false, 6);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (9, 7, '2k5', 'Guada 2005', 'This release places extra emphasis on usability and installability. The installer is adapted from Ubuntu to assume your country, language, keyboard and time zone preference, thus ensuring that installs ask the minimum number of questions possible.', '2005', 2, NULL, 3, 'Guada 2005 is a rapid-install version of
+Ubuntu Hoary for the Andalucian marketplace.', 'Guada2005', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.488598', false, false, NULL, NULL, NULL, false, 4);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (10, 1, 'breezy-autotest', 'Breezy Badger Autotest', 'Autotest version of Breezy', '6.6.6', 1, NULL, 3, 'Autosync uploader test', 'Breezy Badger Autotest', NULL, 0, 8, 'autotest_changes@xxxxxxxxxx', 0, 0, NULL, '2006-10-16 18:31:43.489468', false, false, NULL, NULL, NULL, false, 1);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (11, 8, 'breezy-autotest', 'Breezy Badger Autotest', 'Autotest version of Breezy', '6.6.6', 1, NULL, 1, 'Autosync uploader test', 'Breezy Badger Autotest', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.490333', false, false, NULL, NULL, NULL, false, 1);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (12, 5, 'krunch', 'The Krunchy Kangaroo', 'The archive split allows us to use different kernel settings for Kubuntu, as well as make other pervasive low-level fixes, and release on our own schedule.', '8.06', 1, NULL, 3, 'This is the first experimental release of Kubuntu that uses a separate archive from the main Ubuntu release.', 'Krunch', NULL, 0, NULL, NULL, 0, 0, 33, '2006-10-16 18:31:43.491929', false, false, NULL, NULL, NULL, false, 1);
+INSERT INTO distroseries (id, distribution, name, title, description, version, releasestatus, datereleased, parent_series, summary, displayname, datelastlangpack, messagecount, nominatedarchindep, changeslist, binarycount, sourcecount, driver, date_created, hide_all_translations, defer_translation_imports, language_pack_base, language_pack_delta, language_pack_proposed, language_pack_full_export_requested, registrant) VALUES (13, 8, 'hoary-test', 'Mock Hoary', 'nothing special', '9.9.9', 1, NULL, 1, 'summmmmmmary', 'Hoary Mock', NULL, 0, NULL, NULL, 0, 0, NULL, '2006-10-16 18:31:43.492845', false, false, NULL, NULL, NULL, false, 1);
 
 
 ALTER TABLE distroseries ENABLE TRIGGER ALL;
@@ -1908,21 +1914,21 @@
 
 ALTER TABLE distribution DISABLE TRIGGER ALL;
 
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (1, 'ubuntu', 'Ubuntu Linux', 'Ubuntu is a new approach to Linux Distribution that includes regular releases, and a simplified single-CD installation system.', 'ubuntulinux.org', 17, 'Ubuntu', 'Ubuntu is a new approach to Linux Distribution that includes regular releases, and a simplified single-CD installation system.', 17, NULL, 1, NULL, true, true, NULL, NULL, 3, 59, NULL, NULL, '2006-10-16 18:31:43.415195', NULL, NULL, NULL, NULL, NULL, true, NULL, true, true, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (2, 'redhat', 'Redhat Advanced Server', 'Red Hat is a commercial distribution of the GNU/Linux Operating System.', 'redhat.com', 1, 'Red Hat', 'Red Hat is a commercial distribution of the GNU/Linux Operating System.', 1, NULL, 1, NULL, false, false, NULL, 8, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.417928', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (3, 'debian', 'Debian GNU/Linux', 'Debian GNU/Linux is
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (1, 'ubuntu', 'Ubuntu Linux', 'Ubuntu is a new approach to Linux Distribution that includes regular releases, and a simplified single-CD installation system.', 'ubuntulinux.org', 17, 'Ubuntu', 'Ubuntu is a new approach to Linux Distribution that includes regular releases, and a simplified single-CD installation system.', 17, NULL, 1, NULL, true, true, NULL, NULL, 3, 59, NULL, NULL, '2006-10-16 18:31:43.415195', NULL, NULL, NULL, NULL, NULL, true, NULL, true, true, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (2, 'redhat', 'Redhat Advanced Server', 'Red Hat is a commercial distribution of the GNU/Linux Operating System.', 'redhat.com', 1, 'Red Hat', 'Red Hat is a commercial distribution of the GNU/Linux Operating System.', 1, NULL, 1, NULL, false, false, NULL, 8, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.417928', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (3, 'debian', 'Debian GNU/Linux', 'Debian GNU/Linux is
 a non commercial distribution of a GNU/Linux Operating System for many
 platforms.', 'debian.org', 1, 'Debian', 'Debian GNU/Linux is
 a non commercial distribution of a GNU/Linux Operating System for many
-platforms.', 1, NULL, 1, NULL, false, false, NULL, NULL, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.418942', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (4, 'gentoo', 'The Gentoo Linux', 'Gentoo is a very
+platforms.', 1, NULL, 1, NULL, false, false, NULL, NULL, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.418942', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (4, 'gentoo', 'The Gentoo Linux', 'Gentoo is a very
 customizeable GNU/Linux Distribution that is designed to let you build every
-single package yourself, with your own preferences.', 'gentoo.org', 1, 'Gentoo', 'Gentoo is a very customizeable GNU/Linux Distribution that is designed to let you build every single package yourself, with your own preferences.', 1, NULL, 1, NULL, true, false, NULL, NULL, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.41974', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (5, 'kubuntu', 'Kubuntu - Free KDE-based Linux', 'Kubuntu is an entirely free Linux distribution that uses the K Desktop
+single package yourself, with your own preferences.', 'gentoo.org', 1, 'Gentoo', 'Gentoo is a very customizeable GNU/Linux Distribution that is designed to let you build every single package yourself, with your own preferences.', 1, NULL, 1, NULL, true, false, NULL, NULL, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.41974', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (5, 'kubuntu', 'Kubuntu - Free KDE-based Linux', 'Kubuntu is an entirely free Linux distribution that uses the K Desktop
 Environment as its default desktop after install.', 'kubuntu.org', 1, 'Kubuntu', 'Kubuntu is an entirely free Linux distribution that uses the K Desktop
-Environment as its default desktop after install.', 1, NULL, 1, NULL, false, false, NULL, 8, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.420551', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (7, 'guadalinex', 'GuadaLinex: Linux for Andalucia', 'GuadaLinex is based on Ubuntu and adds full support for applications specific to the local environment in Andalucia.', 'guadalinex.es', 4, 'GuadaLinex', 'The GuadaLinex team produces a high quality linux for the Andalucian marketplace.', 32, NULL, 1, NULL, false, false, NULL, NULL, NULL, 4, NULL, NULL, '2006-10-16 18:31:43.421329', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
-INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage) VALUES (8, 'ubuntutest', 'Ubuntu Test', 'Ubuntu Test', 'ubuntulinux.org', 17, 'ubuntutest', 'Ubuntu Test summary', 17, NULL, 1, NULL, false, false, NULL, NULL, NULL, 17, NULL, NULL, '2006-10-16 18:31:43.422162', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10);
+Environment as its default desktop after install.', 1, NULL, 1, NULL, false, false, NULL, 8, NULL, 1, NULL, NULL, '2006-10-16 18:31:43.420551', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (7, 'guadalinex', 'GuadaLinex: Linux for Andalucia', 'GuadaLinex is based on Ubuntu and adds full support for applications specific to the local environment in Andalucia.', 'guadalinex.es', 4, 'GuadaLinex', 'The GuadaLinex team produces a high quality linux for the Andalucian marketplace.', 32, NULL, 1, NULL, false, false, NULL, NULL, NULL, 4, NULL, NULL, '2006-10-16 18:31:43.421329', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
+INSERT INTO distribution (id, name, title, description, domainname, owner, displayname, summary, members, translationgroup, translationpermission, bug_supervisor, official_malone, official_rosetta, security_contact, driver, translation_focus, mirror_admin, upload_admin, upload_sender, date_created, homepage_content, icon, mugshot, logo, fti, official_answers, language_pack_admin, official_blueprints, enable_bug_expiration, bug_reporting_guidelines, reviewer_whiteboard, max_bug_heat, bug_reported_acknowledgement, answers_usage, blueprints_usage, translations_usage, registrant) VALUES (8, 'ubuntutest', 'Ubuntu Test', 'Ubuntu Test', 'ubuntulinux.org', 17, 'ubuntutest', 'Ubuntu Test summary', 17, NULL, 1, NULL, false, false, NULL, NULL, NULL, 17, NULL, NULL, '2006-10-16 18:31:43.422162', NULL, NULL, NULL, NULL, NULL, false, NULL, false, false, NULL, NULL, NULL, NULL, 10, 10, 10, 60);
 
 
 ALTER TABLE distribution ENABLE TRIGGER ALL;
@@ -2891,54 +2897,54 @@
 
 ALTER TABLE message DISABLE TRIGGER ALL;
 
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (1, '2004-09-24 20:58:04.684057', 'PEBCAK', 16, NULL, NULL, 'foo@xxxxxxxxxxx-332342--1231', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (3, '2004-09-24 21:17:17.153792', 'Reproduced on AIX', 12, NULL, NULL, 'sdsdfsfd', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (4, '2004-09-24 21:24:03.922564', 'Re: Reproduced on AIX', 12, NULL, NULL, 'sdfssfdfsd', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (5, '2004-09-24 21:29:27.407354', 'Fantastic idea, I''d really like to see this', 12, NULL, NULL, 'dxssdfsdgf', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (6, '2004-09-24 21:35:20.125564', 'Strange bug with duplicate messages.', 12, NULL, NULL, 'sdfsfwew', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (7, '2005-01-14 17:20:12.820778', 'Reflow problems with complex page layouts', 12, NULL, NULL, '<20050114172012.6687.51124.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (8, '2005-01-14 17:27:03.702622', 'Firefox install instructions should be complete', 12, NULL, NULL, '<20050114172703.6687.71983.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (9, '2005-01-14 17:35:39.548665', 'Firefox crashes when Save As dialog for a nonexistent window is closed', 12, NULL, NULL, '<20050114173539.6687.81610.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (10, '2004-10-05 00:00:00', 'Re: Bug Title Test', 12, NULL, NULL, '<20050831114528.7616.78129.malone@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (11, '2005-10-14 15:12:29.602117', 'A test bug', 16, NULL, NULL, '<20051014151229.28962.1536.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (12, '2005-10-14 12:25:21.508923', 'Re: Newly installed plug-in doesn''t seem to be used', 16, NULL, NULL, '<20051014122521.14276.39260.lptickets@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (13, '2005-10-14 13:28:11.554476', 'Re: Slow system', 12, NULL, NULL, '<20051014132811.14276.65873.lptickets@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (14, '2005-10-28 09:10:17.13237', 'Printing doesn''t work', 12, NULL, 3, '<20051028091017.6690.9505.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (15, '2006-02-23 16:42:14.080227', 'Thunderbird crashes', 16, NULL, 1, '<20060223164214.9126.7558.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (16, '2006-06-16 17:12:54', 'Unicodeâ„¢', 16, NULL, NULL, '<20060616141252.22134.71562@localhost.localdomain>', NULL, 51);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (17, '2006-02-22 19:42:21.890299', 'another test bug', 16, NULL, 1, '<20060222194221.25842.69665.malonedeb@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (18, '2006-07-20 20:48:24.975495', 'Re: Continue playing after shutdown', 16, NULL, NULL, '<20060720204825.13277.37433.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (19, '2006-07-20 20:49:47.551344', 'Re: mailto: problem in webpage', 16, NULL, NULL, '<20060720204947.13277.79684.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (20, '2006-07-20 20:52:07.054216', 'Re: Installation of Java Runtime Environment for Mozilla', 16, NULL, NULL, '<20060720205207.13277.68582.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (21, '2006-07-20 20:53:53.684848', 'Re: Play DVDs in Totem', 16, NULL, NULL, '<20060720205354.13277.37000.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (22, '2006-07-20 20:56:35.442839', 'Re: mailto: problem in webpage', 12, NULL, NULL, '<20060720205635.13277.87295.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (23, '2006-07-20 23:11:24.975495', 'Re: Continue playing after shutdown', 12, NULL, NULL, '<20061201222020.597.97888.lptickets@xxxxxxxxxxxxxxxxx>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (24, '2007-03-15 20:33:56.67893', 'Make Jokosher use autoaudiosink', 26, NULL, NULL, '<20070315203356.12919.76581.malonedeb@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (25, '2007-03-15 20:34:26.518114', 'Re: Make Jokosher use autoaudiosink', 50, NULL, NULL, '<20070315203426.12919.54628.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (26, '2007-03-15 20:35:10.133383', 'Re: Make Jokosher use autoaudiosink', 66, NULL, NULL, '<20070315203510.12919.22697.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (27, '2007-03-15 20:36:01.779544', 'Autoaudiosink is no longer under development', 63, NULL, NULL, '<20070315203601.12919.29640.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (28, '2007-03-15 20:36:57.133832', 'Re: Autoaudiosink is no longer under development', 27, NULL, NULL, '<20070315203657.12919.48585.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (29, '2007-03-15 20:37:27.991571', 'This is a really new title', 33, NULL, NULL, '<20070315203728.12919.76787.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (30, '2007-03-15 20:37:51.544376', 'Re: Make Jokosher use autoaudiosink', 3, NULL, NULL, '<20070315203751.12919.49072.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (31, '2007-03-15 20:41:18.635493', 'Copy, Cut and Delete operations should work on selections', 8, NULL, NULL, '<20070315204118.14326.61124.malonedeb@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (32, '2007-03-15 20:41:42.154264', 'Re: Copy, Cut and Delete operations should work on selections', 16, NULL, NULL, '<20070315204142.14326.82988.launchpad@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (33, '2007-03-15 20:43:14.981111', 'Re: Copy, Cut and Delete operations should work on selections', 45, NULL, NULL, '<20070315204315.14326.75272.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (34, '2007-03-15 20:45:15.852052', 'Re: Copy, Cut and Delete operations should work on selections', 13, NULL, NULL, '<20070315204515.14326.38817.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (35, '2007-03-15 20:45:51.817826', 'Re: Copy, Cut and Delete operations should work on selections', 9, NULL, NULL, '<20070315204551.14326.36994.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (36, '2007-03-15 20:46:49.83307', 'Re: Copy, Cut and Delete operations should work on selections', 6, NULL, NULL, '<20070315204649.14326.69581.malone@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (37, '2007-07-27 20:00:58.299796', 'Launchpad CSS and JS is not testible', 12, NULL, NULL, '<20070727200058.25131.76173.malonedeb@autumn.annrky-sinzui.local>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (38, '2007-07-27 20:29:46.25854', 'Re: Launchpad CSS and JS is not testible', 12, NULL, NULL, '<20070727202946.25131.16206.malone@autumn.annrky-sinzui.local>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (39, '2007-08-09 11:39:16.836856', 'jokosher exposes personal details in its actions portlet', 63, NULL, NULL, '<20070809113916.26819.83859.malonedeb@localhost.localdomain>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (40, '2004-12-18 16:30:19.103679', 'Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163019.18924.87555.malonedeb@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (41, '2007-12-18 16:30:47.889614', 'Re: Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163048.18924.13348.launchpad@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (42, '2007-12-18 16:30:47.889614', 'Re: Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163048.18924.86681.launchpad@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (43, '2007-12-18 16:31:34.790641', 'Re: Nonsensical bugs are useless', 62, NULL, NULL, '<20071218163134.18996.53651.launchpad@localhost>', NULL, NULL);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (44, '2005-05-13 16:37:37', 'gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, NULL, NULL, '<4284D7D1.6010208@xxxxxx>', NULL, 75);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (45, '2005-05-17 18:54:29', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243615, 44, NULL, '<20050517185429.GB20786@xxxxxxxxxxxxxxx>', NULL, 76);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (46, '2005-05-17 19:24:25', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, 45, NULL, '<428A44E9.6090802@xxxxxx>', NULL, 77);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (47, '2005-05-17 20:20:44', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243615, 46, NULL, '<20050517202044.GA23231@xxxxxxxxxxxxxxx>', NULL, 78);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (48, '2005-06-17 14:00:11', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243616, 46, NULL, '<20050617140011.GA15638@xxxxxxxxx>', NULL, 79);
-INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw) VALUES (49, '2005-06-25 10:13:10', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, NULL, NULL, '<42BD2E36.9090809@xxxxxx>', NULL, 81);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (1, '2004-09-24 20:58:04.684057', 'PEBCAK', 16, NULL, NULL, 'foo@xxxxxxxxxxx-332342--1231', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (3, '2004-09-24 21:17:17.153792', 'Reproduced on AIX', 12, NULL, NULL, 'sdsdfsfd', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (4, '2004-09-24 21:24:03.922564', 'Re: Reproduced on AIX', 12, NULL, NULL, 'sdfssfdfsd', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (5, '2004-09-24 21:29:27.407354', 'Fantastic idea, I''d really like to see this', 12, NULL, NULL, 'dxssdfsdgf', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (6, '2004-09-24 21:35:20.125564', 'Strange bug with duplicate messages.', 12, NULL, NULL, 'sdfsfwew', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (7, '2005-01-14 17:20:12.820778', 'Reflow problems with complex page layouts', 12, NULL, NULL, '<20050114172012.6687.51124.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (8, '2005-01-14 17:27:03.702622', 'Firefox install instructions should be complete', 12, NULL, NULL, '<20050114172703.6687.71983.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (9, '2005-01-14 17:35:39.548665', 'Firefox crashes when Save As dialog for a nonexistent window is closed', 12, NULL, NULL, '<20050114173539.6687.81610.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (10, '2004-10-05 00:00:00', 'Re: Bug Title Test', 12, NULL, NULL, '<20050831114528.7616.78129.malone@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (11, '2005-10-14 15:12:29.602117', 'A test bug', 16, NULL, NULL, '<20051014151229.28962.1536.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (12, '2005-10-14 12:25:21.508923', 'Re: Newly installed plug-in doesn''t seem to be used', 16, NULL, NULL, '<20051014122521.14276.39260.lptickets@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (13, '2005-10-14 13:28:11.554476', 'Re: Slow system', 12, NULL, NULL, '<20051014132811.14276.65873.lptickets@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (14, '2005-10-28 09:10:17.13237', 'Printing doesn''t work', 12, NULL, 3, '<20051028091017.6690.9505.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (15, '2006-02-23 16:42:14.080227', 'Thunderbird crashes', 16, NULL, 1, '<20060223164214.9126.7558.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (16, '2006-06-16 17:12:54', 'Unicodeâ„¢', 16, NULL, NULL, '<20060616141252.22134.71562@localhost.localdomain>', NULL, 51, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (17, '2006-02-22 19:42:21.890299', 'another test bug', 16, NULL, 1, '<20060222194221.25842.69665.malonedeb@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (18, '2006-07-20 20:48:24.975495', 'Re: Continue playing after shutdown', 16, NULL, NULL, '<20060720204825.13277.37433.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (19, '2006-07-20 20:49:47.551344', 'Re: mailto: problem in webpage', 16, NULL, NULL, '<20060720204947.13277.79684.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (20, '2006-07-20 20:52:07.054216', 'Re: Installation of Java Runtime Environment for Mozilla', 16, NULL, NULL, '<20060720205207.13277.68582.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (21, '2006-07-20 20:53:53.684848', 'Re: Play DVDs in Totem', 16, NULL, NULL, '<20060720205354.13277.37000.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (22, '2006-07-20 20:56:35.442839', 'Re: mailto: problem in webpage', 12, NULL, NULL, '<20060720205635.13277.87295.lptickets@xxxxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (23, '2006-07-20 23:11:24.975495', 'Re: Continue playing after shutdown', 12, NULL, NULL, '<20061201222020.597.97888.lptickets@xxxxxxxxxxxxxxxxx>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (24, '2007-03-15 20:33:56.67893', 'Make Jokosher use autoaudiosink', 26, NULL, NULL, '<20070315203356.12919.76581.malonedeb@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (25, '2007-03-15 20:34:26.518114', 'Re: Make Jokosher use autoaudiosink', 50, NULL, NULL, '<20070315203426.12919.54628.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (26, '2007-03-15 20:35:10.133383', 'Re: Make Jokosher use autoaudiosink', 66, NULL, NULL, '<20070315203510.12919.22697.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (27, '2007-03-15 20:36:01.779544', 'Autoaudiosink is no longer under development', 63, NULL, NULL, '<20070315203601.12919.29640.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (28, '2007-03-15 20:36:57.133832', 'Re: Autoaudiosink is no longer under development', 27, NULL, NULL, '<20070315203657.12919.48585.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (29, '2007-03-15 20:37:27.991571', 'This is a really new title', 33, NULL, NULL, '<20070315203728.12919.76787.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (30, '2007-03-15 20:37:51.544376', 'Re: Make Jokosher use autoaudiosink', 3, NULL, NULL, '<20070315203751.12919.49072.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (31, '2007-03-15 20:41:18.635493', 'Copy, Cut and Delete operations should work on selections', 8, NULL, NULL, '<20070315204118.14326.61124.malonedeb@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (32, '2007-03-15 20:41:42.154264', 'Re: Copy, Cut and Delete operations should work on selections', 16, NULL, NULL, '<20070315204142.14326.82988.launchpad@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (33, '2007-03-15 20:43:14.981111', 'Re: Copy, Cut and Delete operations should work on selections', 45, NULL, NULL, '<20070315204315.14326.75272.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (34, '2007-03-15 20:45:15.852052', 'Re: Copy, Cut and Delete operations should work on selections', 13, NULL, NULL, '<20070315204515.14326.38817.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (35, '2007-03-15 20:45:51.817826', 'Re: Copy, Cut and Delete operations should work on selections', 9, NULL, NULL, '<20070315204551.14326.36994.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (36, '2007-03-15 20:46:49.83307', 'Re: Copy, Cut and Delete operations should work on selections', 6, NULL, NULL, '<20070315204649.14326.69581.malone@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (37, '2007-07-27 20:00:58.299796', 'Launchpad CSS and JS is not testible', 12, NULL, NULL, '<20070727200058.25131.76173.malonedeb@autumn.annrky-sinzui.local>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (38, '2007-07-27 20:29:46.25854', 'Re: Launchpad CSS and JS is not testible', 12, NULL, NULL, '<20070727202946.25131.16206.malone@autumn.annrky-sinzui.local>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (39, '2007-08-09 11:39:16.836856', 'jokosher exposes personal details in its actions portlet', 63, NULL, NULL, '<20070809113916.26819.83859.malonedeb@localhost.localdomain>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (40, '2004-12-18 16:30:19.103679', 'Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163019.18924.87555.malonedeb@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (41, '2007-12-18 16:30:47.889614', 'Re: Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163048.18924.13348.launchpad@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (42, '2007-12-18 16:30:47.889614', 'Re: Nonsensical bugs are useless', 16, NULL, NULL, '<20071218163048.18924.86681.launchpad@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (43, '2007-12-18 16:31:34.790641', 'Re: Nonsensical bugs are useless', 62, NULL, NULL, '<20071218163134.18996.53651.launchpad@localhost>', NULL, NULL, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (44, '2005-05-13 16:37:37', 'gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, NULL, NULL, '<4284D7D1.6010208@xxxxxx>', NULL, 75, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (45, '2005-05-17 18:54:29', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243615, 44, NULL, '<20050517185429.GB20786@xxxxxxxxxxxxxxx>', NULL, 76, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (46, '2005-05-17 19:24:25', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, 45, NULL, '<428A44E9.6090802@xxxxxx>', NULL, 77, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (47, '2005-05-17 20:20:44', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243615, 46, NULL, '<20050517202044.GA23231@xxxxxxxxxxxxxxx>', NULL, 78, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (48, '2005-06-17 14:00:11', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243616, 46, NULL, '<20050617140011.GA15638@xxxxxxxxx>', NULL, 79, true);
+INSERT INTO message (id, datecreated, subject, owner, parent, distribution, rfc822msgid, fti, raw, visible) VALUES (49, '2005-06-25 10:13:10', 'Re: Bug#308994: gnome-volume-manager: dvd+rw unreadable when automounted in burner because mounted read/write', 243614, NULL, NULL, '<42BD2E36.9090809@xxxxxx>', NULL, 81, true);
 
 
 ALTER TABLE message ENABLE TRIGGER ALL;
@@ -3336,42 +3342,42 @@
 
 ALTER TABLE bugmessage DISABLE TRIGGER ALL;
 
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (1, 2, 1, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (2, 1, 3, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (3, 1, 4, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (4, 2, 5, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (5, 2, 6, NULL, NULL, true, 2);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (6, 4, 7, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (7, 5, 8, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (8, 6, 9, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (9, 3, 10, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (10, 7, 11, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (11, 8, 14, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (12, 9, 15, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (13, 10, 17, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (14, 10, 16, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (15, 11, 24, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (16, 11, 25, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (17, 11, 26, NULL, NULL, true, 2);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (18, 11, 27, NULL, NULL, true, 3);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (19, 11, 28, NULL, NULL, true, 4);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (20, 11, 29, NULL, NULL, true, 5);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (21, 11, 30, NULL, NULL, true, 6);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (22, 12, 31, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (23, 12, 33, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (24, 12, 34, NULL, NULL, true, 2);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (25, 12, 35, NULL, NULL, true, 3);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (26, 12, 36, NULL, NULL, true, 4);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (27, 13, 37, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (28, 13, 38, NULL, NULL, true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (29, 14, 39, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (30, 15, 40, NULL, NULL, true, 0);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (31, 15, 44, 11, '<4284D7D1.6010208@xxxxxx>', true, 1);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (32, 15, 45, 11, '<20050517185429.GB20786@xxxxxxxxxxxxxxx>', true, 2);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (33, 15, 46, 11, '<428A44E9.6090802@xxxxxx>', true, 3);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (34, 15, 47, 11, '<20050517202044.GA23231@xxxxxxxxxxxxxxx>', true, 4);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (35, 15, 48, 11, '<20050617140011.GA15638@xxxxxxxxx>', true, 5);
-INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, visible, index) VALUES (36, 15, 49, 11, '<42BD2E36.9090809@xxxxxx>', true, 6);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (1, 2, 1, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (2, 1, 3, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (3, 1, 4, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (4, 2, 5, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (5, 2, 6, NULL, NULL, 2);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (6, 4, 7, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (7, 5, 8, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (8, 6, 9, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (9, 3, 10, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (10, 7, 11, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (11, 8, 14, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (12, 9, 15, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (13, 10, 17, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (14, 10, 16, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (15, 11, 24, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (16, 11, 25, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (17, 11, 26, NULL, NULL, 2);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (18, 11, 27, NULL, NULL, 3);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (19, 11, 28, NULL, NULL, 4);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (20, 11, 29, NULL, NULL, 5);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (21, 11, 30, NULL, NULL, 6);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (22, 12, 31, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (23, 12, 33, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (24, 12, 34, NULL, NULL, 2);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (25, 12, 35, NULL, NULL, 3);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (26, 12, 36, NULL, NULL, 4);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (27, 13, 37, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (28, 13, 38, NULL, NULL, 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (29, 14, 39, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (30, 15, 40, NULL, NULL, 0);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (31, 15, 44, 11, '<4284D7D1.6010208@xxxxxx>', 1);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (32, 15, 45, 11, '<20050517185429.GB20786@xxxxxxxxxxxxxxx>', 2);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (33, 15, 46, 11, '<428A44E9.6090802@xxxxxx>', 3);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (34, 15, 47, 11, '<20050517202044.GA23231@xxxxxxxxxxxxxxx>', 4);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (35, 15, 48, 11, '<20050617140011.GA15638@xxxxxxxxx>', 5);
+INSERT INTO bugmessage (id, bug, message, bugwatch, remote_comment_id, index) VALUES (36, 15, 49, 11, '<42BD2E36.9090809@xxxxxx>', 6);
 
 
 ALTER TABLE bugmessage ENABLE TRIGGER ALL;
@@ -4000,6 +4006,13 @@
 ALTER TABLE databasecpustats ENABLE TRIGGER ALL;
 
 
+ALTER TABLE databasediskutilization DISABLE TRIGGER ALL;
+
+
+
+ALTER TABLE databasediskutilization ENABLE TRIGGER ALL;
+
+
 ALTER TABLE databasereplicationlag DISABLE TRIGGER ALL;
 
 
@@ -4389,6 +4402,20 @@
 ALTER TABLE featureflag ENABLE TRIGGER ALL;
 
 
+ALTER TABLE featureflagchangelogentry DISABLE TRIGGER ALL;
+
+
+
+ALTER TABLE featureflagchangelogentry ENABLE TRIGGER ALL;
+
+
+ALTER TABLE featureflagchangelogentry DISABLE TRIGGER ALL;
+
+
+
+ALTER TABLE featureflagchangelogentry ENABLE TRIGGER ALL;
+
+
 ALTER TABLE flatpackagesetinclusion DISABLE TRIGGER ALL;
 
 
@@ -9903,10 +9930,18 @@
 ALTER TABLE projectrelationship ENABLE TRIGGER ALL;
 
 
+ALTER TABLE publisherconfig DISABLE TRIGGER ALL;
+
+INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (1, 1, '/var/tmp/archive', 'http://archive.launchpad.dev/', 'http://rebuild-test.internal/');
+INSERT INTO publisherconfig (id, distribution, root_dir, base_url, copy_base_url) VALUES (2, 8, '/var/tmp/archive', 'http://archive.launchpad.dev/', 'http://rebuild-test.internal/');
+
+
+ALTER TABLE publisherconfig ENABLE TRIGGER ALL;
+
+
 ALTER TABLE pushmirroraccess DISABLE TRIGGER ALL;
 
 
-
 ALTER TABLE pushmirroraccess ENABLE TRIGGER ALL;
 
 

=== modified file 'database/schema/comments.sql'
--- database/schema/comments.sql	2011-02-28 15:51:56 +0000
+++ database/schema/comments.sql	2011-03-23 13:31:30 +0000
@@ -1098,7 +1098,7 @@
 COMMENT ON COLUMN Distribution.reviewer_whiteboard IS 'A whiteboard for Launchpad admins, registry experts and the project owners to capture the state of current issues with the project.';
 COMMENT ON COLUMN Distribution.max_bug_heat IS 'The highest heat value across bugs for this distribution.';
 COMMENT ON COLUMN Distribution.bug_reported_acknowledgement IS 'A message of acknowledgement to display to a bug reporter after they\'ve reported a new bug.';
-
+COMMENT ON COLUMN Distribution.registrant IS 'The person in launchpad who registered this distribution.';
 
 -- DistroSeries
 
@@ -1352,7 +1352,6 @@
 COMMENT ON TABLE BugMessage IS 'This table maps a message to a bug. In other words, it shows that a particular message is associated with a particular bug.';
 COMMENT ON COLUMN BugMessage.bugwatch IS 'The external bug this bug comment was imported from.';
 COMMENT ON COLUMN BugMessage.remote_comment_id IS 'The id this bug comment has in the external bug tracker, if it is an imported comment. If it is NULL while having a bugwatch set, this comment was added in Launchpad and needs to be pushed to the external bug tracker.';
-COMMENT ON COLUMN BugMessage.visible IS 'If false, the bug comment is hidden and should not be shown in any UI.';
 COMMENT ON COLUMN BugMessage.index IS 'The index (used in urls) of the message in a particular bug.';
 
 -- Messaging subsytem
@@ -1361,6 +1360,7 @@
 COMMENT ON COLUMN Message.subject IS 'The title text of the message, or the subject if it was an email.';
 COMMENT ON COLUMN Message.distribution IS 'The distribution in which this message originated, if we know it.';
 COMMENT ON COLUMN Message.raw IS 'The original unadulterated message if it arrived via email. This is required to provide access to the original, undecoded message.';
+COMMENT ON COLUMN Message.visible IS 'If false, the message is hidden and should not be shown in any UI.';
 
 COMMENT ON TABLE MessageChunk IS 'This table stores a single chunk of a possibly multipart message. There will be at least one row in this table for each message. text/* parts are stored in the content column. All other parts are stored in the Librarian and referenced via the blob column. If both content and blob are NULL, then this chunk has been removed (eg. offensive, legal reasons, virus etc.)';
 COMMENT ON COLUMN MessageChunk.content IS 'Text content for this chunk of the message. This content is full text searchable.';
@@ -1621,7 +1621,7 @@
 COMMENT ON COLUMN DistroSeries.releasestatus IS 'The current release status of this distroseries. E.g. "pre-release freeze" or "released"';
 COMMENT ON COLUMN DistroSeries.datereleased IS 'The date on which this distroseries was released. (obviously only valid for released distributions)';
 COMMENT ON COLUMN DistroSeries.parent_series IS 'The parent distroseries on which this distribution is based. This is related to the inheritance stuff.';
-COMMENT ON COLUMN DistroSeries.owner IS 'The ultimate owner of this distroseries.';
+COMMENT ON COLUMN DistroSeries.registrant IS 'The user who registered this distroseries.';
 COMMENT ON COLUMN DistroSeries.driver IS 'This is a person or team who can act as a driver for this specific release - note that the distribution drivers can also set goals for any release.';
 COMMENT ON COLUMN DistroSeries.changeslist IS 'The email address (name name) of the changes announcement list for this distroseries. If NULL, no announcement mail will be sent.';
 COMMENT ON COLUMN DistroSeries.defer_translation_imports IS 'Don''t accept PO imports for this release just now.';

=== modified file 'database/schema/launchpad_session.sql'
--- database/schema/launchpad_session.sql	2010-09-10 09:45:45 +0000
+++ database/schema/launchpad_session.sql	2011-03-23 13:31:30 +0000
@@ -29,3 +29,28 @@
 GRANT SELECT, INSERT, UPDATE, DELETE ON TimeLimitedToken TO session;
 -- And the garbo needs to run on it too.
 GRANT SELECT, DELETE ON TimeLimitedToken TO session;
+
+
+-- This helper needs to exist in the session database so the BulkPruner
+-- can clean up unwanted sessions.
+CREATE OR REPLACE FUNCTION cursor_fetch(cur refcursor, n integer)
+RETURNS SETOF record LANGUAGE plpgsql AS
+$$
+DECLARE
+    r record;
+    count integer;
+BEGIN
+    FOR count IN 1..n LOOP
+        FETCH FORWARD FROM cur INTO r;
+        IF NOT FOUND THEN
+            RETURN;
+        END IF;
+        RETURN NEXT r;
+    END LOOP;
+END;
+$$;
+
+COMMENT ON FUNCTION cursor_fetch(refcursor, integer) IS
+'Fetch the next n items from a cursor. Work around for not being able to use FETCH inside a SELECT statement.';
+
+GRANT EXECUTE ON FUNCTION cursor_fetch(refcursor, integer) TO session;

=== added file 'database/schema/patch-2208-51-0.sql'
--- database/schema/patch-2208-51-0.sql	1970-01-01 00:00:00 +0000
+++ database/schema/patch-2208-51-0.sql	2011-03-23 13:31:30 +0000
@@ -0,0 +1,18 @@
+SET client_min_messages=ERROR;
+
+--Create new visible column on message.
+ALTER TABLE Message
+    ADD COLUMN visible BOOLEAN NOT NULL DEFAULT TRUE;
+
+--Migrate the data, rebuilding indexes afterwards.
+UPDATE Message SET visible = FALSE
+    FROM BugMessage
+    WHERE BugMessage.message = Message.id AND BugMessage.visible IS FALSE;
+CLUSTER Message USING message_pkey;
+
+--And kill the old column
+ALTER TABLE BugMessage
+    DROP COLUMN visible;
+
+--Per patch adding reqs
+INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 51, 0);

=== added file 'database/schema/patch-2208-52-0.sql'
--- database/schema/patch-2208-52-0.sql	1970-01-01 00:00:00 +0000
+++ database/schema/patch-2208-52-0.sql	2011-03-23 13:31:30 +0000
@@ -0,0 +1,14 @@
+SET client_min_messages=ERROR;
+
+CREATE TABLE PublisherConfig (
+    id serial PRIMARY KEY,
+    distribution integer NOT NULL CONSTRAINT publisherconfig__distribution__fk REFERENCES distribution,
+    root_dir text NOT NULL,
+    base_url text NOT NULL,
+    copy_base_url text NOT NULL
+);
+    
+CREATE UNIQUE INDEX publisherconfig__distribution__idx
+    ON PublisherConfig(distribution);
+
+INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 52, 0);

=== added file 'database/schema/patch-2208-53-0.sql'
--- database/schema/patch-2208-53-0.sql	1970-01-01 00:00:00 +0000
+++ database/schema/patch-2208-53-0.sql	2011-03-23 13:31:30 +0000
@@ -0,0 +1,20 @@
+-- Copyright 2009 Canonical Ltd.  This software is licensed under the
+-- GNU Affero General Public License version 3 (see the file LICENSE).
+
+SET client_min_messages=ERROR;
+
+-- Add a registrant column to distributions.
+ALTER TABLE Distribution
+    ADD COLUMN registrant integer REFERENCES Person;
+
+-- Set registrant to owner for existing distros.
+update Distribution
+    SET registrant = owner;
+
+-- Add NOT NULL constraint to registrant column.
+ALTER TABLE Distribution ALTER COLUMN registrant SET NOT NULL;
+
+-- Add index to registrant column.
+CREATE INDEX distribution__registrant__idx ON Distribution(registrant);
+
+INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 53, 0);

=== added file 'database/schema/patch-2208-54-0.sql'
--- database/schema/patch-2208-54-0.sql	1970-01-01 00:00:00 +0000
+++ database/schema/patch-2208-54-0.sql	2011-03-23 13:31:30 +0000
@@ -0,0 +1,6 @@
+SET client_min_messages=ERROR;
+
+ALTER TABLE distroarchseries
+    ADD CONSTRAINT valid_architecturetag CHECK (valid_name(architecturetag));
+
+INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 54, 0);

=== added file 'database/schema/patch-2208-55-0.sql'
--- database/schema/patch-2208-55-0.sql	1970-01-01 00:00:00 +0000
+++ database/schema/patch-2208-55-0.sql	2011-03-23 13:31:30 +0000
@@ -0,0 +1,24 @@
+-- Copyright 2011 Canonical Ltd. This software is licensed under the
+-- GNU Affero General Public License version 3 (see the file LICENSE).
+
+SET client_min_messages=ERROR;
+
+-- Convert DistroSeriesDifference source_version, parent_source_version,
+-- and base_version types to debversion.
+
+-- Change types.
+ALTER TABLE DistroSeriesDifference
+    ALTER COLUMN source_version TYPE debversion,
+    ALTER COLUMN parent_source_version TYPE debversion,
+    ALTER COLUMN base_version TYPE debversion;
+
+-- Create indexes.
+CREATE INDEX distroseriesdifference__source_version__idx
+    ON DistroSeriesDifference(source_version);
+CREATE INDEX distroseriesdifference__parent_source_version__idx
+    ON DistroSeriesDifference(parent_source_version);
+CREATE INDEX distroseriesdifference__base_version__idx
+    ON DistroSeriesDifference(base_version);
+
+INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 55, 0);
+

=== added file 'database/schema/patch-2208-56-0.sql'
--- database/schema/patch-2208-56-0.sql	1970-01-01 00:00:00 +0000
+++ database/schema/patch-2208-56-0.sql	2011-03-23 13:31:30 +0000
@@ -0,0 +1,33 @@
+-- Copyright 2009 Canonical Ltd.  This software is licensed under the
+-- GNU Affero General Public License version 3 (see the file LICENSE).
+
+SET client_min_messages=ERROR;
+
+-- Renaming owner to registrant for DistroSeries
+
+-- Rename owner into registrant.
+ALTER TABLE distroseries 
+    RENAME COLUMN owner TO registrant;
+
+-- 'Rename' constraint.
+ALTER TABLE distroseries 
+    ADD CONSTRAINT distroseries__registrant__fk 
+    FOREIGN KEY (registrant) REFERENCES Person(id);
+ALTER TABLE distroseries
+    DROP CONSTRAINT distroseries__owner__fk;
+
+-- Rename index.
+ALTER INDEX distroseries__owner__idx 
+    RENAME TO distroseries__registrant__idx;
+
+-- Rename old misnamed indexes.
+-- Don't rename primary key indexes though, as this causes Slony-I to explode.
+--ALTER INDEX distrorelease_pkey
+--    RENAME TO distroseries_pkey;
+ALTER INDEX distrorelease_distribution_key
+    RENAME TO distrorelease__distribution__name__key;
+ALTER INDEX distrorelease_distro_release_unique
+    RENAME TO distroseries__distribution__id__key;
+
+
+INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 56, 0);

=== added file 'database/schema/patch-2208-57-0.sql'
--- database/schema/patch-2208-57-0.sql	1970-01-01 00:00:00 +0000
+++ database/schema/patch-2208-57-0.sql	2011-03-23 13:31:30 +0000
@@ -0,0 +1,14 @@
+-- Copyright 2011 Canonical Ltd.  This software is licensed under the
+-- GNU Affero General Public License version 3 (see the file LICENSE).
+
+SET client_min_messages=ERROR;
+
+-- Partial index for queue processing view : the lopsided data makes this
+-- necessary (millions of rows matching the archive, 100's of rows match the
+-- query).
+
+
+CREATE INDEX packageupload__id_distroseries__archive__idx ON
+    packageupload(id, distroseries, archive) WHERE status IN (0,1);
+
+INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 57, 0);

=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg	2011-03-22 01:15:32 +0000
+++ database/schema/security.cfg	2011-03-23 13:31:30 +0000
@@ -260,6 +260,7 @@
 public.productrelease                   = SELECT, INSERT, UPDATE, DELETE
 public.productreleasefile               = SELECT, INSERT, DELETE
 public.productseriescodeimport          = SELECT, INSERT, UPDATE
+public.publisherconfig                  = SELECT, INSERT, UPDATE, DELETE
 public.project                          = SELECT
 public.projectbounty                    = SELECT, INSERT, UPDATE
 public.questionbug                      = SELECT, INSERT, DELETE
@@ -859,6 +860,8 @@
 public.packagesetgroup                          = SELECT
 public.packagesetsources                        = SELECT, INSERT, UPDATE, DELETE
 public.packagesetinclusion                      = SELECT, INSERT, UPDATE, DELETE
+# INSERT for publisherconfig only required for the test suite.
+public.publisherconfig                          = SELECT, INSERT
 public.flatpackagesetinclusion                  = SELECT, INSERT, UPDATE, DELETE
 public.binarypackagepublishinghistory           = SELECT, INSERT, UPDATE, DELETE
 public.sourcepackagepublishinghistory           = SELECT, INSERT, UPDATE, DELETE
@@ -961,6 +964,7 @@
 public.teamparticipation                        = SELECT
 public.translationimportqueueentry              = SELECT, INSERT, UPDATE
 public.translationtemplatesbuild                = SELECT, INSERT
+public.publisherconfig                          = SELECT
 
 [ppa-apache-log-parser]
 type=user
@@ -1430,6 +1434,7 @@
 public.componentselection               = SELECT
 public.sectionselection                 = SELECT
 public.packagediff                      = SELECT, UPDATE
+public.publisherconfig                  = SELECT
 
 # Librarian stuff
 public.libraryfilealias                 = SELECT, INSERT
@@ -2274,6 +2279,7 @@
 public.distroseries                     = SELECT
 public.emailaddress                     = SELECT
 public.person                           = SELECT
+public.publisherconfig                  = SELECT
 public.teammembership                   = SELECT
 public.teamparticipation                = SELECT
 

=== modified file 'database/schema/security.py'
--- database/schema/security.py	2010-11-11 14:13:19 +0000
+++ database/schema/security.py	2011-03-23 13:31:30 +0000
@@ -19,7 +19,6 @@
 
 from canonical.database.sqlbase import connect
 from canonical.launchpad.scripts import logger_options, logger, db_options
-from lp.services.log.loglevels import DEBUG2
 from fti import quote_identifier
 import replication.helpers
 
@@ -36,6 +35,7 @@
 
 
 class DbObject(object):
+
     def __init__(
             self, schema, name, type_, owner, arguments=None, language=None):
         self.schema = schema
@@ -50,9 +50,7 @@
 
     @property
     def fullname(self):
-        fn = "%s.%s" % (
-                self.schema, self.name
-                )
+        fn = "%s.%s" % (self.schema, self.name)
         if self.type == 'function':
             fn = "%s(%s)" % (fn, self.arguments)
         return fn
@@ -67,6 +65,7 @@
 class DbSchema(dict):
     groups = None # List of groups defined in the db
     users = None # List of users defined in the db
+
     def __init__(self, con):
         super(DbSchema, self).__init__()
         cur = con.cursor()
@@ -113,8 +112,7 @@
                 """)
         for schema, name, arguments, owner, language in cur.fetchall():
             self['%s.%s(%s)' % (schema, name, arguments)] = DbObject(
-                    schema, name, 'function', owner, arguments, language
-                    )
+                    schema, name, 'function', owner, arguments, language)
         # Pull a list of groups
         cur.execute("SELECT groname FROM pg_group")
         self.groups = [r[0] for r in cur.fetchall()]
@@ -129,6 +127,7 @@
 
 
 class CursorWrapper(object):
+
     def __init__(self, cursor):
         self.__dict__['_cursor'] = cursor
 
@@ -149,7 +148,7 @@
 
 
 CONFIG_DEFAULTS = {
-    'groups': ''
+    'groups': '',
     }
 
 
@@ -192,6 +191,120 @@
         quote_identifier(identifier) for identifier in identifiers])
 
 
+class PermissionGatherer:
+    """Gather permissions for bulk granting or revocation.
+
+    Processing such statements in bulk (with multiple users, tables,
+    or permissions in one statement) is faster than issuing very large
+    numbers of individual statements.
+    """
+
+    def __init__(self, entity_keyword):
+        """Gather for SQL entities of one kind (TABLE, FUNCTION, SEQUENCE).
+
+        :param entity_keyword: The SQL keyword for the kind of entity
+            that permissions will be gathered for.
+        """
+        self.entity_keyword = entity_keyword
+        self.permissions = defaultdict(dict)
+
+    def add(self, permission, entity, principal, is_group=False):
+        """Add a permission.
+
+        Add all privileges you want to grant or revoke first, then use
+        `grant` or `revoke` to process them in bulk.
+
+        :param permission: A permission: SELECT, INSERT, EXECUTE, etc.
+        :param entity: Table, function, or sequence on which to grant
+            or revoke a privilege.
+        :param principal: User or group to which the privilege should
+            apply.
+        :param is_group: Is `principal` a group?
+        """
+        if is_group:
+            full_principal = "GROUP " + principal
+        else:
+            full_principal = principal
+        self.permissions[permission].setdefault(entity, set()).add(
+            full_principal)
+
+    def tabulate(self):
+        """Group privileges into single-statement work items.
+
+        Each entry returned by this method represents a batch of
+        privileges that can be granted or revoked in a single SQL
+        statement.
+
+        :return: A sequence of tuples of strings: permission(s) to
+            grant/revoke, entity or entities to act on, and principal(s)
+            to grant or revoke for.  Each is a string.
+        """
+        result = []
+        for permission, parties in self.permissions.iteritems():
+            for entity, principals in parties.iteritems():
+                result.append(
+                    (permission, entity, ", ".join(principals)))
+        return result
+
+    def countPermissions(self):
+        """Count the number of different permissions."""
+        return len(self.permissions)
+
+    def countEntities(self):
+        """Count the number of different entities."""
+        return len(set(sum([
+            entities.keys()
+            for entities in self.permissions.itervalues()], [])))
+
+    def countPrincipals(self):
+        """Count the number of different principals."""
+        principals = set()
+        for entities_and_principals in self.permissions.itervalues():
+            for extra_principals in entities_and_principals.itervalues():
+                principals.update(extra_principals)
+        return len(principals)
+
+    def grant(self, cur):
+        """Grant all gathered permissions.
+
+        :param cur: A cursor to operate on.
+        """
+        log.debug(
+            "Granting %d permission(s) on %d %s(s) for %d user(s)/group(s).",
+            self.countPermissions(),
+            self.countEntities(),
+            self.entity_keyword,
+            self.countPrincipals())
+        grant_count = 0
+        for permissions, entities, principals in self.tabulate():
+            grant = "GRANT %s ON %s %s TO %s" % (
+                permissions, self.entity_keyword, entities, principals)
+            log.debug2(grant)
+            cur.execute(grant)
+            grant_count += 1
+        log.debug("Issued %d GRANT statement(s).", grant_count)
+
+    def revoke(self, cur):
+        """Revoke all gathered permissions.
+
+        :param cur: A cursor to operate on.
+        """
+        log.debug(
+            "Revoking %d permission(s) on %d %s(s) for %d user(s)/group(s).",
+            self.countPermissions(),
+            self.countEntities(),
+            self.entity_keyword,
+            self.countPrincipals())
+        revoke_count = 0
+        for permissions, entities, principals in self.tabulate():
+            revoke = "REVOKE %s ON %s %s FROM %s" % (
+                permissions, self.entity_keyword, entities, principals)
+            log.debug2(revoke)
+            cur.execute(revoke)
+            revoke_count += 1
+        log.debug("Issued %d REVOKE statement(s).", revoke_count)
+
+
 def reset_permissions(con, config, options):
     schema = DbSchema(con)
     all_users = list_identifiers(schema.users)
@@ -264,8 +377,7 @@
             continue
         groups = [
             g.strip() for g in config.get(user, 'groups', '').split(',')
-            if g.strip()
-            ]
+            if g.strip()]
         # Read-Only users get added to Read-Only groups.
         if user.endswith('_ro'):
             groups = ['%s_ro' % group for group in groups]
@@ -287,36 +399,37 @@
                 cur.execute("ALTER TABLE %s OWNER TO %s" % (
                     obj.fullname, quote_identifier(options.owner)))
 
-    # Revoke all privs from known groups. Don't revoke anything for
-    # users or groups not defined in our security.cfg.
-    revocations = defaultdict(list)
-    # Gather all revocations.
-    for section_name in config.sections():
-        for obj in schema.values():
-            if obj.type == 'function':
-                t = 'FUNCTION'
-            else:
-                t = 'TABLE'
-
-            item = "%s %s" % (t, obj.fullname)
-
-            roles = [section_name]
-            if section_name != 'public':
-                roles.append(section_name + '_ro')
-
-            revocations[item] += roles
-
-            if schema.has_key(obj.seqname):
-                revocations["SEQUENCE %s" % obj.seqname] += roles
-
-    # Now batch up and execute all revocations.
     if options.revoke:
-        for item, roles in revocations.iteritems():
-            if roles:
-                log.debug("Revoking permissions on %s", item)
-                cur.execute(
-                    "REVOKE ALL ON %s FROM %s"
-                    % (item, list_identifiers(roles)))
+        # Revoke all privs from known groups. Don't revoke anything for
+        # users or groups not defined in our security.cfg.
+        table_revocations = PermissionGatherer("TABLE")
+        function_revocations = PermissionGatherer("FUNCTION")
+        sequence_revocations = PermissionGatherer("SEQUENCE")
+
+        # Gather all revocations.
+        for section_name in config.sections():
+            role = quote_identifier(section_name)
+            if section_name == 'public':
+                ro_role = None
+            else:
+                ro_role = quote_identifier(section_name + "_ro")
+
+            for obj in schema.values():
+                if obj.type == 'function':
+                    gatherer = function_revocations
+                else:
+                    gatherer = table_revocations
+
+                gatherer.add("ALL", obj.fullname, role)
+
+                if obj.seqname in schema:
+                    sequence_revocations.add("ALL", obj.seqname, role)
+                    if ro_role is not None:
+                        sequence_revocations.add("ALL", obj.seqname, ro_role)
+
+        table_revocations.revoke(cur)
+        function_revocations.revoke(cur)
+        sequence_revocations.revoke(cur)
     else:
         log.info("Not revoking permissions on database objects")
 
@@ -327,8 +440,9 @@
 
     # Set permissions as per config file
 
-    functions = set()
-    tables = set()
+    table_permissions = PermissionGatherer("TABLE")
+    function_permissions = PermissionGatherer("FUNCTION")
+    sequence_permissions = PermissionGatherer("SEQUENCE")
 
     for username in config.sections():
         for obj_name, perm in config.items(username):
@@ -355,73 +469,42 @@
             log.debug(
                 "Granting %s on %s to %s", perm, obj.fullname, who)
             if obj.type == 'function':
-                functions.add(obj.fullname)
-                cur.execute(
-                    'GRANT %s ON FUNCTION %s TO %s'
-                    % (perm, obj.fullname, who))
-                cur.execute(
-                    'GRANT EXECUTE ON FUNCTION %s TO GROUP %s'
-                    % (obj.fullname, who_ro))
+                function_permissions.add(perm, obj.fullname, who)
+                function_permissions.add("EXECUTE", obj.fullname, who_ro)
+                function_permissions.add(
+                    "EXECUTE", obj.fullname, "read", is_group=True)
+                function_permissions.add(
+                    "ALL", obj.fullname, "admin", is_group=True)
             else:
-                tables.add(obj.fullname)
-                cur.execute(
-                    'GRANT %s ON TABLE %s TO %s'
-                    % (perm, obj.fullname, who))
-                cur.execute(
-                    'GRANT SELECT ON TABLE %s TO %s'
-                    % (obj.fullname, who_ro))
-                if schema.has_key(obj.seqname):
+                table_permissions.add(
+                    "ALL", obj.fullname, "admin", is_group=True)
+                table_permissions.add(perm, obj.fullname, who)
+                table_permissions.add("SELECT", obj.fullname, who_ro)
+                is_secure = (obj.fullname in SECURE_TABLES)
+                if not is_secure:
+                    table_permissions.add(
+                        "SELECT", obj.fullname, "read", is_group=True)
+                if obj.seqname in schema:
                     if 'INSERT' in perm:
                         seqperm = 'USAGE'
                     elif 'SELECT' in perm:
                         seqperm = 'SELECT'
-                    log.debug(
-                        "Granting %s on %s to %s", seqperm, obj.seqname, who)
-                    cur.execute(
-                        'GRANT %s ON %s TO %s'
-                        % (seqperm, obj.seqname, who))
-                    if obj.fullname not in SECURE_TABLES:
-                        cur.execute(
-                            'GRANT SELECT ON %s TO GROUP read'
-                            % obj.seqname)
-                    cur.execute(
-                        'GRANT ALL ON %s TO GROUP admin'
-                        % obj.seqname)
-                    cur.execute(
-                        'GRANT SELECT ON %s TO %s'
-                        % (obj.seqname, who_ro))
+                    sequence_permissions.add(seqperm, obj.seqname, who)
+                    if not is_secure:
+                        sequence_permissions.add(
+                            "SELECT", obj.seqname, "read", is_group=True)
+                    sequence_permissions.add("SELECT", obj.seqname, who_ro)
+                    sequence_permissions.add(
+                        "ALL", obj.seqname, "admin", is_group=True)
 
-    # A few groups get special rights to every function or table.  Batch
-    # the schema manipulations to save time.
-    log.debug(
-        "Granting permissions to %d functions to magic roles",
-        len(functions))
-    if functions:
-        functions_text = ', '.join(functions)
-        cur.execute(
-            "GRANT EXECUTE ON FUNCTION %s TO GROUP read" % functions_text)
-        cur.execute(
-            "GRANT ALL ON FUNCTION %s TO GROUP admin" % functions_text)
-    log.debug(
-        "Granting permissions to %d tables to admin role",
-        len(tables))
-    if tables:
-        tables_text = ', '.join(tables)
-        cur.execute("GRANT ALL ON TABLE %s TO GROUP admin" % tables_text)
-    nonsecure_tables = tables - set(SECURE_TABLES)
-    log.debug(
-        "Granting permissions to %d nonsecure tables to read role",
-        len(nonsecure_tables))
-    if nonsecure_tables:
-        nonsecure_tables_text = ', '.join(nonsecure_tables)
-        cur.execute(
-            "GRANT SELECT ON TABLE %s TO GROUP read" % nonsecure_tables_text)
+    function_permissions.grant(cur)
+    table_permissions.grant(cur)
+    sequence_permissions.grant(cur)
 
     # Set permissions on public schemas
     public_schemas = [
-        s.strip() for s in config.get('DEFAULT','public_schemas').split(',')
-        if s.strip()
-        ]
+        s.strip() for s in config.get('DEFAULT', 'public_schemas').split(',')
+        if s.strip()]
     log.debug("Granting access to %d public schemas", len(public_schemas))
     for schema_name in public_schemas:
         cur.execute("GRANT USAGE ON SCHEMA %s TO PUBLIC" % (
@@ -444,7 +527,7 @@
         if obj not in found:
             forgotten.add(obj)
     forgotten = [obj.fullname for obj in forgotten
-        if obj.type in ['table','function','view']]
+        if obj.type in ['table', 'function', 'view']]
     if forgotten:
         log.warn('No permissions specified for %r', forgotten)
 

=== modified file 'lib/canonical/config/schema-lazr.conf'
--- lib/canonical/config/schema-lazr.conf	2011-03-18 01:17:42 +0000
+++ lib/canonical/config/schema-lazr.conf	2011-03-23 13:31:30 +0000
@@ -24,14 +24,11 @@
 # datatype: string
 dbuser: archivepublisher
 
-# Base directory for distribution archives.
-# datatype: string
-root: /var/tmp/archive/
-
-# External base URL for distribution archives.
-# datatype: string
-base_url: http://ftpmaster.internal/
-copy_base_url: http://rebuild-test.internal/
+# XXX: wgrant 2011-03-22 bug=739992: These three keys are obsolete and
+# should be removed after 11.04 is released.
+root: none
+base_url: none 
+copy_base_url: none
 
 
 [binaryfile_expire]

=== modified file 'lib/canonical/launchpad/database/message.py'
--- lib/canonical/launchpad/database/message.py	2011-03-04 03:48:11 +0000
+++ lib/canonical/launchpad/database/message.py	2011-03-23 13:31:30 +0000
@@ -37,6 +37,7 @@
     )
 import pytz
 from sqlobject import (
+    BoolCol,
     ForeignKey,
     IntCol,
     SQLMultipleJoin,
@@ -131,6 +132,7 @@
     raw = ForeignKey(foreignKey='LibraryFileAlias', dbName='raw',
                      default=None)
     bugattachments = SQLMultipleJoin('BugAttachment', joinColumn='_message')
+    visible = BoolCol(notNull=True, default=True)
 
     def __repr__(self):
         return "<Message at 0x%x id=%s>" % (id(self), self.id)

=== modified file 'lib/canonical/launchpad/interfaces/message.py'
--- lib/canonical/launchpad/interfaces/message.py	2010-10-03 15:30:06 +0000
+++ lib/canonical/launchpad/interfaces/message.py	2011-03-23 13:31:30 +0000
@@ -104,6 +104,8 @@
         title=_("Whether or not the title of this message "
                 "is different to that of its parent."),
         readonly=True)
+    visible = Bool(title=u"This message is visible or not.", required=False,
+        default=True)
 
     bugattachments = exported(
         CollectionField(

=== modified file 'lib/canonical/launchpad/pagetests/basics/notfound-traversals.txt'
--- lib/canonical/launchpad/pagetests/basics/notfound-traversals.txt	2011-03-15 08:24:29 +0000
+++ lib/canonical/launchpad/pagetests/basics/notfound-traversals.txt	2011-03-23 13:31:30 +0000
@@ -222,7 +222,7 @@
 >>> check("/ubuntu/hoary")
 >>> check("/ubuntu/hoary/+edit", auth=True)
 >>> check("/ubuntu/hoary/+specs")
->>> check("/ubuntu/hoary/+reassign", auth=True)
+>>> check_not_found("/ubuntu/hoary/+reassign")
 >>> check("/ubuntu/hoary/+packaging")
 >>> check("/ubuntu/hoary/+bugs")
 

=== modified file 'lib/canonical/launchpad/pagetests/webservice/xx-hide-comments.txt'
--- lib/canonical/launchpad/pagetests/webservice/xx-hide-comments.txt	2010-10-11 16:17:45 +0000
+++ lib/canonical/launchpad/pagetests/webservice/xx-hide-comments.txt	2011-03-23 13:31:30 +0000
@@ -27,7 +27,7 @@
     >>> bug_11 = getUtility(IBugSet).get(11)
     >>> bug_message = getUtility(IBugMessageSet).getByBugAndMessage(
     ...     bug_11, bug_11.messages[2])
-    >>> bug_message.visible
+    >>> bug_message.message.visible
     False
     >>> logout()
 
@@ -46,7 +46,7 @@
     >>> bug_11 = getUtility(IBugSet).get(11)
     >>> bug_message = getUtility(IBugMessageSet).getByBugAndMessage(
     ...     bug_11, bug_11.messages[2])
-    >>> bug_message.visible
+    >>> bug_message.message.visible
     True
     >>> logout()
 

=== modified file 'lib/canonical/launchpad/security.py'
--- lib/canonical/launchpad/security.py	2011-02-21 15:22:33 +0000
+++ lib/canonical/launchpad/security.py	2011-03-23 13:31:30 +0000
@@ -35,6 +35,7 @@
     IOAuthAccessToken,
     IOAuthRequestToken,
     )
+from canonical.launchpad.interfaces.message import IMessage
 from canonical.launchpad.webapp.authorization import check_permission
 from canonical.launchpad.webapp.interfaces import (
     IAuthorization,
@@ -45,6 +46,7 @@
 from lp.answers.interfaces.question import IQuestion
 from lp.answers.interfaces.questionsperson import IQuestionsPerson
 from lp.answers.interfaces.questiontarget import IQuestionTarget
+from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfig
 from lp.blueprints.interfaces.specification import (
     ISpecification,
     ISpecificationPublic,
@@ -985,9 +987,10 @@
     usedfor = IDistroSeries
 
 
-class EditDistroSeriesByOwnersOrDistroOwnersOrAdmins(AuthorizationBase):
-    """The owner of the distro series should be able to modify some of the
-    fields on the IDistroSeries
+class EditDistroSeriesByReleaseManagerOrDistroOwnersOrAdmins(
+    AuthorizationBase):
+    """The owner of the distro series (i.e. the owner of the distribution)
+    should be able to modify some of the fields on the IDistroSeries
 
     NB: there is potential for a great mess if this is not done correctly so
     please consult with Kiko and MDZ on the mailing list before modifying
@@ -1002,8 +1005,7 @@
             # The series driver (release manager) may edit a series if the
             # distribution is an `IDerivativeDistribution`
             return True
-        return (user.inTeam(self.obj.owner) or
-                user.inTeam(self.obj.distribution.owner) or
+        return (user.inTeam(self.obj.distribution.owner) or
                 user.in_admin)
 
 
@@ -2052,7 +2054,7 @@
 
 class AdminDistroSeriesLanguagePacks(
     OnlyRosettaExpertsAndAdmins,
-    EditDistroSeriesByOwnersOrDistroOwnersOrAdmins):
+    EditDistroSeriesByReleaseManagerOrDistroOwnersOrAdmins):
     permission = 'launchpad.LanguagePacksAdmin'
     usedfor = IDistroSeries
 
@@ -2063,10 +2065,10 @@
         edit distroseries or members of IDistribution.language_pack_admin team
         are able to change the language packs available.
         """
+        EditDS = EditDistroSeriesByReleaseManagerOrDistroOwnersOrAdmins
         return (
             OnlyRosettaExpertsAndAdmins.checkAuthenticated(self, user) or
-            EditDistroSeriesByOwnersOrDistroOwnersOrAdmins.checkAuthenticated(
-                self, user) or
+            EditDS.checkAuthenticated(self, user) or
             user.inTeam(self.obj.distribution.language_pack_admin))
 
 
@@ -2587,3 +2589,16 @@
         if parent is None:
             return False
         return check_permission(self.permission, parent)
+
+
+class SetMessageVisibility(AuthorizationBase):
+    permission = 'launchpad.Admin'
+    usedfor = IMessage
+
+    def checkAuthenticated(self, user):
+        """Admins and registry admins can set bug comment visibility."""
+        return (user.in_admin or user.in_registry_experts)
+
+
+class ViewPublisherConfig(AdminByAdminsTeam):
+    usedfor = IPublisherConfig

=== modified file 'lib/canonical/launchpad/zcml/message.zcml'
--- lib/canonical/launchpad/zcml/message.zcml	2010-10-03 15:30:06 +0000
+++ lib/canonical/launchpad/zcml/message.zcml	2011-03-23 13:31:30 +0000
@@ -9,11 +9,12 @@
     i18n_domain="launchpad">
 
     <!-- Message -->
-    <class class="canonical.launchpad.database.message.Message">
+    <class
+        class="canonical.launchpad.database.message.Message">
         <allow interface="canonical.launchpad.interfaces.message.IMessage" />
         <require
-            permission="zope.Public"
-            set_schema="canonical.launchpad.interfaces.message.IMessage" />
+            permission="launchpad.Admin"
+            set_attributes="visible"/>
     </class>
 
     <class class="canonical.launchpad.interfaces.message.IndexedMessage">

=== modified file 'lib/lp/app/stories/launchpad-root/site-search.txt'
--- lib/lp/app/stories/launchpad-root/site-search.txt	2011-02-17 17:02:54 +0000
+++ lib/lp/app/stories/launchpad-root/site-search.txt	2011-03-23 13:31:30 +0000
@@ -181,9 +181,11 @@
     >>> print_search_results()
     Exact matches
     Ubuntu
-    Ubuntu is a new approach to Linux Distribution that includes regular
-    releases, and a simplified single-CD installation system.
-    Registered on 2006-10-16
+    Ubuntu is a new approach to Linux Distribution that includes ...
+    Registered
+    by
+    Registry Administrators
+    on 2006-10-16
 
 The user enters the number 1, and he sees a bug and a question in the
 "Exact matches" section.

=== modified file 'lib/lp/archivepublisher/config.py'
--- lib/lp/archivepublisher/config.py	2010-10-17 13:35:20 +0000
+++ lib/lp/archivepublisher/config.py	2011-03-23 13:31:30 +0000
@@ -8,7 +8,10 @@
 
 import os
 
+from zope.component import getUtility
+
 from canonical.config import config
+from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
 from lp.soyuz.enums import ArchivePurpose
 
 
@@ -24,9 +27,11 @@
     """
     pubconf = Config()
     ppa_config = config.personalpackagearchive
+    db_pubconf = getUtility(
+        IPublisherConfigSet).getByDistribution(archive.distribution)
 
     pubconf.temproot = os.path.join(
-        config.archivepublisher.root, '%s-temp' % archive.distribution.name)
+        db_pubconf.root_dir, '%s-temp' % archive.distribution.name)
 
     if archive.is_ppa:
         if archive.private:
@@ -40,7 +45,7 @@
             pubconf.distroroot, archive.owner.name, archive.name,
             archive.distribution.name)
     elif archive.is_main:
-        pubconf.distroroot = config.archivepublisher.root
+        pubconf.distroroot = db_pubconf.root_dir
         pubconf.archiveroot = os.path.join(
             pubconf.distroroot, archive.distribution.name)
         if archive.purpose == ArchivePurpose.PARTNER:
@@ -48,7 +53,7 @@
         elif archive.purpose == ArchivePurpose.DEBUG:
             pubconf.archiveroot += '-debug'
     elif archive.is_copy:
-        pubconf.distroroot = config.archivepublisher.root
+        pubconf.distroroot = db_pubconf.root_dir
         pubconf.archiveroot = os.path.join(
             pubconf.distroroot,
             archive.distribution.name + '-' + archive.name,
@@ -83,10 +88,6 @@
     return pubconf
 
 
-class LucilleConfigError(Exception):
-    """Lucille configuration was not present."""
-
-
 class Config(object):
     """Manage a publisher configuration from the database. (Read Only)
     This class provides a useful abstraction so that if we change

=== modified file 'lib/lp/archivepublisher/deathrow.py'
--- lib/lp/archivepublisher/deathrow.py	2010-10-17 13:35:20 +0000
+++ lib/lp/archivepublisher/deathrow.py	2011-03-23 13:31:30 +0000
@@ -17,7 +17,6 @@
 from lp.archivepublisher import ELIGIBLE_DOMINATION_STATES
 from lp.archivepublisher.config import (
     getPubConfig,
-    LucilleConfigError,
     )
 from lp.archivepublisher.diskpool import DiskPool
 from lp.archivepublisher.utils import process_in_batches
@@ -40,12 +39,8 @@
          the one provided by the publishing-configuration, it will be only
          used for PRIMARY archives.
     """
-    log.debug("Grab Lucille config.")
-    try:
-        pubconf = getPubConfig(archive)
-    except LucilleConfigError, info:
-        log.error(info)
-        raise
+    log.debug("Grab publisher config.")
+    pubconf = getPubConfig(archive)
 
     if (pool_root_override is not None and
         archive.purpose == ArchivePurpose.PRIMARY):

=== added file 'lib/lp/archivepublisher/interfaces/publisherconfig.py'
--- lib/lp/archivepublisher/interfaces/publisherconfig.py	1970-01-01 00:00:00 +0000
+++ lib/lp/archivepublisher/interfaces/publisherconfig.py	2011-03-23 13:31:30 +0000
@@ -0,0 +1,58 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+# pylint: disable-msg=E0211,E0213
+
+"""PublisherConfig interface."""
+
+__metaclass__ = type
+
+__all__ = [
+    'IPublisherConfig',
+    'IPublisherConfigSet',
+    ]
+
+from lazr.restful.fields import Reference
+from zope.interface import Interface
+from zope.schema import (
+    Int,
+    TextLine,
+    )
+
+from canonical.launchpad import _
+from lp.registry.interfaces.distribution import IDistribution
+
+
+class IPublisherConfig(Interface):
+    """`PublisherConfig` interface."""
+
+    id = Int(title=_('ID'), required=True, readonly=True)
+
+    distribution = Reference(
+        IDistribution, title=_("Distribution"), required=True,
+        description=_("The Distribution for this configuration."))
+
+    root_dir = TextLine(
+        title=_("Root Directory"), required=True,
+        description=_("The root directory for published archives."))
+
+    base_url = TextLine(
+        title=_("Base URL"), required=True,
+        description=_("The base URL for published archives"))
+
+    copy_base_url = TextLine(
+        title=_("Copy Base URL"), required=True,
+        description=_("The base URL for published copy archives"))
+
+
+class IPublisherConfigSet(Interface):
+    """`PublisherConfigSet` interface."""
+
+    def new(distribution, root_dir, base_url, copy_base_url):
+        """Create a new `PublisherConfig`."""
+
+    def getByDistribution(distribution):
+        """Get the config for a a distribution.
+
+        :param distribution: An `IDistribution`
+        """

=== added file 'lib/lp/archivepublisher/model/publisherconfig.py'
--- lib/lp/archivepublisher/model/publisherconfig.py	1970-01-01 00:00:00 +0000
+++ lib/lp/archivepublisher/model/publisherconfig.py	2011-03-23 13:31:30 +0000
@@ -0,0 +1,68 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Database class for table PublisherConfig."""
+
+__metaclass__ = type
+
+__all__ = [
+    'PublisherConfig',
+    'PublisherConfigSet',
+    ]
+
+from storm.locals import (
+    Int,
+    Reference,
+    Storm,
+    Unicode,
+    )
+from zope.interface import implements
+
+from canonical.launchpad.interfaces.lpstorm import (
+    IMasterStore,
+    )
+from lp.archivepublisher.interfaces.publisherconfig import (
+    IPublisherConfig,
+    IPublisherConfigSet,
+    )
+
+
+class PublisherConfig(Storm):
+    """See `IArchiveAuthToken`."""
+    implements(IPublisherConfig)
+    __storm_table__ = 'PublisherConfig'
+
+    id = Int(primary=True)
+
+    distribution_id = Int(name='distribution', allow_none=False)
+    distribution = Reference(distribution_id, 'Distribution.id')
+
+    root_dir = Unicode(name='root_dir', allow_none=False)
+
+    base_url = Unicode(name='base_url', allow_none=False)
+
+    copy_base_url = Unicode(name='copy_base_url', allow_none=False)
+
+
+class PublisherConfigSet:
+    """See `IPublisherConfigSet`."""
+    implements(IPublisherConfigSet)
+    title = "Soyuz Publisher Configurations"
+
+    def new(self, distribution, root_dir, base_url, copy_base_url):
+        """Make and return a new `PublisherConfig`."""
+        store = IMasterStore(PublisherConfig)
+        pubconf = PublisherConfig()
+        pubconf.distribution = distribution
+        pubconf.root_dir = root_dir
+        pubconf.base_url = base_url
+        pubconf.copy_base_url = copy_base_url
+        store.add(pubconf)
+        return pubconf
+
+    def getByDistribution(self, distribution):
+        """See `IArchiveAuthTokenSet`."""
+        store = IMasterStore(PublisherConfig)
+        return store.find(
+            PublisherConfig,
+            PublisherConfig.distribution_id == distribution.id).one()

=== modified file 'lib/lp/archivepublisher/publishing.py'
--- lib/lp/archivepublisher/publishing.py	2011-02-04 09:07:36 +0000
+++ lib/lp/archivepublisher/publishing.py	2011-03-23 13:31:30 +0000
@@ -21,7 +21,6 @@
 from lp.archivepublisher import HARDCODED_COMPONENT_ORDER
 from lp.archivepublisher.config import (
     getPubConfig,
-    LucilleConfigError,
     )
 from lp.archivepublisher.diskpool import DiskPool
 from lp.archivepublisher.domination import Dominator
@@ -120,11 +119,7 @@
     else:
         log.debug("Finding configuration for '%s' PPA."
                   % archive.owner.name)
-    try:
-        pubconf = getPubConfig(archive)
-    except LucilleConfigError, info:
-        log.error(info)
-        raise
+    pubconf = getPubConfig(archive)
 
     disk_pool = _getDiskPool(pubconf, log)
 

=== added file 'lib/lp/archivepublisher/tests/test_publisherconfig.py'
--- lib/lp/archivepublisher/tests/test_publisherconfig.py	1970-01-01 00:00:00 +0000
+++ lib/lp/archivepublisher/tests/test_publisherconfig.py	2011-03-23 13:31:30 +0000
@@ -0,0 +1,92 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for publisherConfig model class."""
+
+__metaclass__ = type
+
+
+from storm.store import Store
+from storm.exceptions import IntegrityError
+from zope.component import getUtility
+from zope.interface.verify import verifyObject
+from zope.security.interfaces import Unauthorized
+
+from canonical.launchpad.ftests import login
+from canonical.testing.layers import (
+    DatabaseFunctionalLayer,
+    ZopelessDatabaseLayer,
+    )
+from lp.archivepublisher.interfaces.publisherconfig import (
+    IPublisherConfig,
+    IPublisherConfigSet,
+    )
+from lp.testing import (
+    ANONYMOUS,
+    TestCaseWithFactory,
+    )
+from lp.testing.sampledata import LAUNCHPAD_ADMIN
+
+
+class TestPublisherConfig(TestCaseWithFactory):
+    """Test the `PublisherConfig` model."""
+    layer = ZopelessDatabaseLayer
+
+    def setUp(self):
+        TestCaseWithFactory.setUp(self)
+        self.distribution = self.factory.makeDistribution(name='conftest')
+
+    def test_verify_interface(self):
+        # Test the interface for the model.
+        pubconf = self.factory.makePublisherConfig()
+        verified = verifyObject(IPublisherConfig, pubconf)
+        self.assertTrue(verified)
+
+    def test_properties(self):
+        # Test the model properties.
+        ROOT_DIR = u"rootdir/test"
+        BASE_URL = u"http://base.url";
+        COPY_BASE_URL = u"http://base.url";
+        pubconf = self.factory.makePublisherConfig(
+            distribution=self.distribution,
+            root_dir=ROOT_DIR,
+            base_url=BASE_URL,
+            copy_base_url=COPY_BASE_URL,
+            )
+
+        self.assertEqual(self.distribution.name, pubconf.distribution.name)
+        self.assertEqual(ROOT_DIR, pubconf.root_dir)
+        self.assertEqual(BASE_URL, pubconf.base_url)
+        self.assertEqual(COPY_BASE_URL, pubconf.copy_base_url)
+
+    def test_one_config_per_distro(self):
+        # Only one config for each distro is allowed.
+        pubconf = self.factory.makePublisherConfig(self.distribution)
+        pubconf2 = self.factory.makePublisherConfig(self.distribution)
+        store = Store.of(pubconf)
+        self.assertRaises(IntegrityError, store.flush)
+
+    def test_getByDistribution(self):
+        # Test that IPublisherConfigSet.getByDistribution works.
+        pubconf = getUtility(IPublisherConfigSet).getByDistribution(
+            self.distribution)
+        self.assertEqual(self.distribution.name, pubconf.distribution.name)
+
+
+class TestPublisherConfigSecurity(TestCaseWithFactory):
+
+    layer = DatabaseFunctionalLayer
+
+    def test_only_admin(self):
+        # Only admins can see and change the config.
+        distro = self.factory.makeDistribution(publish_root_dir=u"foo")
+        config = getUtility(IPublisherConfigSet).getByDistribution(distro)
+
+        login(ANONYMOUS)
+        self.assertRaises(Unauthorized, getattr, config, "root_dir")
+        self.assertRaises(Unauthorized, setattr, config, "root_dir", "test")
+
+        login(LAUNCHPAD_ADMIN)
+        self.assertEqual(u"foo", config.root_dir)
+        config.root_dir = u"bar"
+        self.assertEqual(u"bar", config.root_dir)

=== modified file 'lib/lp/archivepublisher/zcml/configure.zcml'
--- lib/lp/archivepublisher/zcml/configure.zcml	2009-07-13 18:15:02 +0000
+++ lib/lp/archivepublisher/zcml/configure.zcml	2011-03-23 13:31:30 +0000
@@ -2,8 +2,32 @@
      GNU Affero General Public License version 3 (see the file LICENSE).
 -->
 
-<configure xmlns="http://namespaces.zope.org/zope";>
+<configure
+    xmlns="http://namespaces.zope.org/zope";
+    xmlns:browser="http://namespaces.zope.org/browser";
+    xmlns:i18n="http://namespaces.zope.org/i18n";
+    xmlns:webservice="http://namespaces.canonical.com/webservice";
+    xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc";
+    i18n_domain="launchpad">
+
     <include package="lp.archivepublisher.zcml"
              file="archivesigningkey.zcml" />
+
+    <securedutility
+        class="lp.archivepublisher.model.publisherconfig.PublisherConfigSet"
+        provides="lp.archivepublisher.interfaces.publisherconfig.IPublisherConfigSet">
+        <allow
+            interface="lp.archivepublisher.interfaces.publisherconfig.IPublisherConfigSet"/>
+    </securedutility>
+
+    <class
+        class="lp.archivepublisher.model.publisherconfig.PublisherConfig">
+        <require
+            permission="launchpad.Admin"
+            interface="lp.archivepublisher.interfaces.publisherconfig.IPublisherConfig"
+            set_schema="lp.archivepublisher.interfaces.publisherconfig.IPublisherConfig"/>
+    </class>
+
+
 </configure>
 

=== modified file 'lib/lp/bugs/browser/bugcomment.py'
--- lib/lp/bugs/browser/bugcomment.py	2011-02-16 20:06:40 +0000
+++ lib/lp/bugs/browser/bugcomment.py	2011-03-23 13:31:30 +0000
@@ -68,7 +68,7 @@
         bug_comment = comments.get(message.id)
         if bug_comment is None:
             bug_comment = BugComment(bugmessage.index, message, bugtask,
-                visible=bugmessage.visible)
+                visible=message.visible)
             comments[message.id] = bug_comment
             # This code path is currently only used from BugTask view which
             # have already loaded all the bug watches. If we start lazy loading

=== modified file 'lib/lp/bugs/configure.zcml'
--- lib/lp/bugs/configure.zcml	2011-03-23 02:54:56 +0000
+++ lib/lp/bugs/configure.zcml	2011-03-23 13:31:30 +0000
@@ -862,7 +862,7 @@
             interface="lp.bugs.interfaces.bugmessage.IBugMessage"/>
         <require
             permission="launchpad.Admin"
-            set_attributes="remote_comment_id bugwatch visible index"/>
+            set_attributes="remote_comment_id bugwatch index"/>
     </class>
 
     <!-- BugMessageSet -->
@@ -1053,6 +1053,10 @@
         class="lp.bugs.mail.bugnotificationrecipients.BugNotificationRecipients">
         <allow
             interface="canonical.launchpad.interfaces.launchpad.INotificationRecipientSet"/>
+        <!-- BugNotificationRecipients provides the following
+             attributes/methods in addition. -->
+        <allow
+            attributes="subscription_filters addFilter"/>
     </class>
     <securedutility
         provides="lp.bugs.interfaces.bugnotification.IBugNotificationSet"

=== modified file 'lib/lp/bugs/doc/bugmessage-visibility.txt'
--- lib/lp/bugs/doc/bugmessage-visibility.txt	2010-10-18 22:24:59 +0000
+++ lib/lp/bugs/doc/bugmessage-visibility.txt	2011-03-23 13:31:30 +0000
@@ -20,27 +20,28 @@
     ...     content="new message for visibility test",
     ...     owner=sample_person,
     ...     bug=bug_one)
-    >>> new_message.visible
+    >>> new_message.message.visible
     True
 
-Only Admins can hide bug messages using the visible field.
+Only Admins and registry experts can hide bug messages using the visible
+field.
 
     # Login an Admin user and set visible to False.
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> abugmessage = bugmessageset.get(1)
-    >>> abugmessage.visible
+    >>> abugmessage.message.visible
     True
-    >>> abugmessage.visible = False
-    >>> abugmessage.visible
+    >>> abugmessage.message.visible = False
+    >>> abugmessage.message.visible
     False
 
     # Non-privileged users should not be able to
     # make comments visible again.
     >>> login('no-priv@xxxxxxxxxxxxx')
     >>> abugmessage = bugmessageset.get(1)
-    >>> abugmessage.visible
+    >>> abugmessage.message.visible
     False
-    >>> abugmessage.visible = True
+    >>> abugmessage.message.visible = True
     Traceback (most recent call last):
     ...
-    Unauthorized: (<BugMessage at ...>, 'visible', 'launchpad.Admin')
+    Unauthorized: (<Message at ...>, 'visible', 'launchpad.Admin')

=== modified file 'lib/lp/bugs/doc/bugnotification-sending.txt'
--- lib/lp/bugs/doc/bugnotification-sending.txt	2011-03-19 14:12:00 +0000
+++ lib/lp/bugs/doc/bugnotification-sending.txt	2011-03-23 13:31:30 +0000
@@ -21,8 +21,10 @@
 
     >>> def print_notification_headers(email_notification):
     ...     for header in ['To', 'From', 'Subject',
-    ...                    'X-Launchpad-Message-Rationale']:
-    ...         print "%s: %s" % (header, email_notification[header])
+    ...                    'X-Launchpad-Message-Rationale',
+    ...                    'X-Launchpad-Subscription']:
+    ...         if email_notification[header]:
+    ...             print "%s: %s" % (header, email_notification[header])
 
     >>> def print_notification(email_notification):
     ...     print_notification_headers(email_notification)
@@ -676,7 +678,6 @@
 been set properly.
 
     >>> from lp.bugs.model.bugnotification import BugNotification
-    >>> from lp.bugs.enum import BugNotificationStatus
     >>> for notification in BugNotification.select(orderBy='id')[-8:]:
     ...     if notification.is_comment:
     ...         identifier = 'comment'
@@ -1247,6 +1248,7 @@
     >>> with lp_dbuser():
     ...     filter = subscription_no_priv.newBugFilter()
     ...     filter.bug_notification_level = BugNotificationLevel.COMMENTS
+    ...     filter.description = u"Allow-comments filter"
 
     >>> comment = getUtility(IMessageSet).fromText(
     ...     'subject', 'another comment.', sample_person,
@@ -1279,12 +1281,14 @@
     From: Sample Person <...@bugs.launchpad.net>
     Subject: [Bug 1] subject
     X-Launchpad-Message-Rationale: Subscriber (Mozilla Firefox)
+    X-Launchpad-Subscription: Allow-comments filter
     <BLANKLINE>
     another comment.
     <BLANKLINE>
     --
     You received this bug notification because you are subscribed to Mozilla
     Firefox.
+    Matching subscriptions: Allow-comments filter
     ...
     ----------------------------------------------------------------------
     To: support@xxxxxxxxxx
@@ -1390,6 +1394,7 @@
     From: Sample Person <...@bugs.launchpad.net>
     Subject: [Bug 1] subject
     X-Launchpad-Message-Rationale: Subscriber (Mozilla Firefox)
+    X-Launchpad-Subscription: Allow-comments filter
     <BLANKLINE>
     no comment for no-priv.
     <BLANKLINE>
@@ -1400,6 +1405,7 @@
     --
     You received this bug notification because you are subscribed to Mozilla
     Firefox.
+    Matching subscriptions: Allow-comments filter
     ...
     ----------------------------------------------------------------------
     To: support@xxxxxxxxxx

=== modified file 'lib/lp/bugs/enum.py'
--- lib/lp/bugs/enum.py	2011-03-05 00:06:26 +0000
+++ lib/lp/bugs/enum.py	2011-03-23 13:31:30 +0000
@@ -57,18 +57,18 @@
 
 class BugNotificationStatus(DBEnumeratedType):
     """The status of a bug notification.
-    
+
     A notification may be pending, sent, or omitted."""
 
     PENDING = DBItem(10, """
         Pending
-        
+
         The notification has not yet been sent.
         """)
 
     OMITTED = DBItem(20, """
         Omitted
-        
+
         The system considered sending the notification, but omitted it.
         This is generally because the action reported by the notification
         was immediately undone.
@@ -76,6 +76,6 @@
 
     SENT = DBItem(30, """
         Sent
-        
+
         The notification has been sent.
         """)

=== modified file 'lib/lp/bugs/interfaces/bug.py'
--- lib/lp/bugs/interfaces/bug.py	2011-03-17 14:26:42 +0000
+++ lib/lp/bugs/interfaces/bug.py	2011-03-23 13:31:30 +0000
@@ -382,7 +382,7 @@
         title=_('Heat Last Updated'), required=False, readonly=True)
 
     # Adding related BugMessages provides a hook for getting at
-    # BugMessage.visible when building bug comments.
+    # BugMessage.message.visible when building bug comments.
     bug_messages = Attribute('The bug messages related to this object.')
     comment_count = Attribute(
         "The number of comments on this bug, not including the initial "

=== modified file 'lib/lp/bugs/interfaces/bugmessage.py'
--- lib/lp/bugs/interfaces/bugmessage.py	2011-02-24 15:30:54 +0000
+++ lib/lp/bugs/interfaces/bugmessage.py	2011-03-23 13:31:30 +0000
@@ -52,8 +52,6 @@
     bugwatchID = Int(title=u'The bugwatch id.', readonly=True)
     remote_comment_id = TextLine(
         title=u"The id this comment has in the bugwatch's bug tracker.")
-    visible = Bool(title=u"This message is visible or not.", required=False,
-        default=True)
 
 
 class IBugMessageSet(Interface):

=== modified file 'lib/lp/bugs/mail/bugnotificationrecipients.py'
--- lib/lp/bugs/mail/bugnotificationrecipients.py	2011-03-02 14:07:23 +0000
+++ lib/lp/bugs/mail/bugnotificationrecipients.py	2011-03-23 13:31:30 +0000
@@ -58,6 +58,7 @@
         """
         NotificationRecipientSet.__init__(self)
         self.duplicateof = duplicateof
+        self.subscription_filters = set()
 
     def _addReason(self, person, reason, header):
         """Adds a reason (text and header) for a person.
@@ -127,3 +128,13 @@
         else:
             text = "are the registrant for %s" % upstream.displayname
         self._addReason(person, text, reason)
+
+    def update(self, recipient_set):
+        """See `INotificationRecipientSet`."""
+        super(BugNotificationRecipients, self).update(recipient_set)
+        self.subscription_filters.update(
+            recipient_set.subscription_filters)
+
+    def addFilter(self, subscription_filter):
+        if subscription_filter is not None:
+            self.subscription_filters.add(subscription_filter)

=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py	2011-03-23 03:21:33 +0000
+++ lib/lp/bugs/model/bug.py	2011-03-23 13:31:30 +0000
@@ -1794,7 +1794,7 @@
         bug_message_set = getUtility(IBugMessageSet)
         bug_message = bug_message_set.getByBugAndMessage(
             self, self.messages[comment_number])
-        bug_message.visible = visible
+        bug_message.message.visible = visible
 
     @cachedproperty
     def _known_viewers(self):

=== modified file 'lib/lp/bugs/model/bugmessage.py'
--- lib/lp/bugs/model/bugmessage.py	2011-02-16 03:35:46 +0000
+++ lib/lp/bugs/model/bugmessage.py	2011-03-23 13:31:30 +0000
@@ -44,7 +44,6 @@
     bugwatch = ForeignKey(dbName='bugwatch', foreignKey='BugWatch',
         notNull=False, default=None)
     remote_comment_id = StringCol(notNull=False, default=None)
-    visible = BoolCol(notNull=True, default=True)
     # -- The index of the message is cached in the DB.
     index = IntCol(notNull=True)
 

=== modified file 'lib/lp/bugs/model/bugnotification.py'
--- lib/lp/bugs/model/bugnotification.py	2011-02-25 16:19:58 +0000
+++ lib/lp/bugs/model/bugnotification.py	2011-03-23 13:31:30 +0000
@@ -150,12 +150,23 @@
             reason_body, reason_header = recipients.getReason(recipient)
             sql_values.append('(%s, %s, %s, %s)' % sqlvalues(
                 bug_notification, recipient, reason_header, reason_body))
+
         # We add all the recipients in a single SQL statement to make
         # this a bit more efficient for bugs with many subscribers.
         store.execute("""
             INSERT INTO BugNotificationRecipient
               (bug_notification, person, reason_header, reason_body)
             VALUES %s;""" % ', '.join(sql_values))
+
+        if len(recipients.subscription_filters) > 0:
+            filter_link_sql = [
+                "(%s, %s)" % sqlvalues(bug_notification, filter.id)
+                for filter in recipients.subscription_filters]
+            store.execute("""
+                INSERT INTO BugNotificationFilter
+                  (bug_notification, bug_subscription_filter)
+                VALUES %s;""" % ", ".join(filter_link_sql))
+
         return bug_notification
 
 

=== modified file 'lib/lp/bugs/model/structuralsubscription.py'
--- lib/lp/bugs/model/structuralsubscription.py	2011-03-11 22:15:40 +0000
+++ lib/lp/bugs/model/structuralsubscription.py	2011-03-23 13:31:30 +0000
@@ -611,14 +611,15 @@
     else:
         subscribers = []
         query_results = source.find(
-            (Person, StructuralSubscription),
-            *constraints).config(distinct=True)
-        for person, subscription in query_results:
+            (Person, StructuralSubscription, BugSubscriptionFilter),
+            *constraints)
+        for person, subscription, filter in query_results:
             # Set up results.
             if person not in recipients:
                 subscribers.append(person)
                 recipients.addStructuralSubscriber(
                     person, subscription.target)
+            recipients.addFilter(filter)
         return subscribers
 
 

=== modified file 'lib/lp/bugs/security.py'
--- lib/lp/bugs/security.py	2011-02-28 22:09:39 +0000
+++ lib/lp/bugs/security.py	2011-03-23 13:31:30 +0000
@@ -161,20 +161,15 @@
     usedfor = IMessage
 
 
-class SetMessageVisibility(AuthorizationBase):
+class SetBugCommentVisibility(AuthorizationBase):
     permission = 'launchpad.Admin'
-    usedfor = IBugMessage
+    usedfor = IBug
 
     def checkAuthenticated(self, user):
         """Admins and registry admins can set bug comment visibility."""
         return (user.in_admin or user.in_registry_experts)
 
 
-class SetBugCommentVisibility(SetMessageVisibility):
-
-    usedfor = IBug
-
-
 class ViewBugTracker(AnonymousAuthorization):
     """Anyone can view a bug tracker."""
     usedfor = IBugTracker

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-hidden-comments.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-hidden-comments.txt	2010-10-11 16:17:45 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-hidden-comments.txt	2011-03-23 13:31:30 +0000
@@ -30,7 +30,7 @@
     This comment will not be visible when the test completes.
     >>> bug_message = getUtility(IBugMessageSet).getByBugAndMessage(
     ...     bug_11, message)
-    >>> bug_message.visible = False
+    >>> bug_message.message.visible = False
     >>> transaction.commit()
     >>> logout()
 

=== modified file 'lib/lp/bugs/tests/test_bug_messages_webservice.py'
--- lib/lp/bugs/tests/test_bug_messages_webservice.py	2011-03-07 13:57:30 +0000
+++ lib/lp/bugs/tests/test_bug_messages_webservice.py	2011-03-23 13:31:30 +0000
@@ -61,7 +61,7 @@
         with person_logged_in(self.admin):
             bug_message = bug_msg_set.getByBugAndMessage(
                 self.bug, self.message)
-            self.assertFalse(bug_message.visible)
+            self.assertFalse(bug_message.message.visible)
 
     def test_random_user_cannot_set_visible(self):
         # Logged in users without privs can't set bug comment

=== modified file 'lib/lp/code/model/tests/test_recipebuilder.py'
--- lib/lp/code/model/tests/test_recipebuilder.py	2010-12-23 01:02:00 +0000
+++ lib/lp/code/model/tests/test_recipebuilder.py	2011-03-23 13:31:30 +0000
@@ -21,7 +21,7 @@
 from zope.security.proxy import removeSecurityProxy
 
 from canonical.testing.layers import (
-    LaunchpadFunctionalLayer,
+    LaunchpadZopelessLayer,
     )
 from lp.buildmaster.enums import BuildFarmJobType
 from lp.buildmaster.interfaces.builder import CannotBuild
@@ -50,7 +50,7 @@
 
 class TestRecipeBuilder(TestCaseWithFactory):
 
-    layer = LaunchpadFunctionalLayer
+    layer = LaunchpadZopelessLayer
 
     def makeJob(self, recipe_registrant=None, recipe_owner=None):
         """Create a sample `ISourcePackageRecipeBuildJob`."""

=== modified file 'lib/lp/registry/adapters.py'
--- lib/lp/registry/adapters.py	2011-01-04 16:08:57 +0000
+++ lib/lp/registry/adapters.py	2011-03-23 13:31:30 +0000
@@ -18,6 +18,9 @@
 from zope.interface import implements
 
 from canonical.launchpad.webapp.interfaces import ILaunchpadPrincipal
+from lp.archivepublisher.interfaces.publisherconfig import (
+    IPublisherConfigSet,
+    )
 from lp.registry.interfaces.poll import (
     IPollSet,
     IPollSubset,
@@ -112,3 +115,10 @@
     or `ILaunchpadUsage`.
     """
     return productseries.product
+
+
+def distribution_to_publisherconfig(distro):
+    """Adapts `IDistribution` to `IPublisherConfig`."""
+    # Used for traversal from distro to +pubconf.
+    config = getUtility(IPublisherConfigSet).getByDistribution(distro)
+    return config

=== modified file 'lib/lp/registry/browser/configure.zcml'
--- lib/lp/registry/browser/configure.zcml	2011-01-24 20:53:10 +0000
+++ lib/lp/registry/browser/configure.zcml	2011-03-23 13:31:30 +0000
@@ -129,13 +129,6 @@
         template="../templates/distroseries-needs-packaging.pt"/>
     <browser:page
         for="lp.registry.interfaces.distroseries.IDistroSeries"
-        permission="launchpad.Admin"
-        facet="overview"
-        class="canonical.launchpad.browser.ObjectReassignmentView"
-        name="+reassign"
-        template="../../../canonical/launchpad/templates/object-reassignment.pt"/>
-    <browser:page
-        for="lp.registry.interfaces.distroseries.IDistroSeries"
         permission="launchpad.Edit"
         name="+edit"
         class="lp.registry.browser.distroseries.DistroSeriesEditView"
@@ -1948,7 +1941,7 @@
         for="lp.registry.interfaces.distribution.IDistribution"
         permission="launchpad.Edit"
         facet="overview"
-        class="canonical.launchpad.browser.ObjectReassignmentView">
+        class="lp.registry.browser.distribution.DistributionReassignmentView">
         <browser:page
             name="+reassign"
             template="../../../canonical/launchpad/templates/object-reassignment.pt"/>
@@ -1992,6 +1985,13 @@
         facet="overview"
         permission="launchpad.Edit"
         template="../../app/templates/generic-edit.pt"/>
+    <browser:page
+        name="+pubconf"
+        for="lp.registry.interfaces.distribution.IDistribution"
+        class="lp.registry.browser.distribution.DistributionPublisherConfigView"
+        template="../../app/templates/generic-edit.pt"
+        facet="overview"
+        permission="launchpad.Admin"/>
     <browser:defaultView
         for="lp.registry.interfaces.distribution.IDistributionSet"
         name="+index"/>

=== modified file 'lib/lp/registry/browser/distribution.py'
--- lib/lp/registry/browser/distribution.py	2011-03-04 09:55:17 +0000
+++ lib/lp/registry/browser/distribution.py	2011-03-23 13:31:30 +0000
@@ -21,6 +21,8 @@
     'DistributionPPASearchView',
     'DistributionPackageSearchView',
     'DistributionPendingReviewMirrorsView',
+    'DistributionPublisherConfigView',
+    'DistributionReassignmentView',
     'DistributionSeriesView',
     'DistributionSeriesMirrorsRSSView',
     'DistributionSeriesMirrorsView',
@@ -40,6 +42,7 @@
 
 from zope.component import getUtility
 from zope.event import notify
+from zope.formlib import form
 from zope.interface import implements
 from zope.lifecycleevent import ObjectCreatedEvent
 from zope.security.interfaces import Unauthorized
@@ -78,6 +81,10 @@
     )
 from lp.app.errors import NotFoundError
 from lp.app.widgets.image import ImageChangeWidget
+from lp.archivepublisher.interfaces.publisherconfig import (
+    IPublisherConfig,
+    IPublisherConfigSet,
+    )
 from lp.blueprints.browser.specificationtarget import (
     HasSpecificationsMenuMixin,
     )
@@ -92,6 +99,7 @@
     RegistryCollectionActionMenuBase,
     )
 from lp.registry.browser.pillar import PillarBugsMenu
+from lp.registry.browser.objectreassignment import ObjectReassignmentView
 from lp.registry.interfaces.distribution import (
     IDerivativeDistribution,
     IDistribution,
@@ -278,7 +286,12 @@
     """A menu of context actions."""
     usedfor = IDistribution
     facet = 'overview'
-    links = ['edit']
+    links = ['edit', 'pubconf']
+
+    @enabled_with_permission("launchpad.Admin")
+    def pubconf(self):
+        text = "Configure publisher"
+        return Link("+pubconf", text, icon="edit")
 
 
 class DistributionOverviewMenu(ApplicationMenu, DistributionLinksMixin):
@@ -325,7 +338,7 @@
 
     @enabled_with_permission('launchpad.Edit')
     def reassign(self):
-        text = 'Change registrant'
+        text = 'Change maintainer'
         return Link('+reassign', text, icon='edit')
 
     def newmirror(self):
@@ -770,6 +783,7 @@
             domainname=data['domainname'],
             members=data['members'],
             owner=self.user,
+            registrant=self.user,
             )
         notify(ObjectCreatedEvent(distribution))
         self.next_url = canonical_url(distribution)
@@ -1081,3 +1095,62 @@
     @cachedproperty
     def mirrors(self):
         return self.context.disabled_mirrors
+
+
+class DistributionReassignmentView(ObjectReassignmentView):
+    """View class for changing distribution maintainer."""
+    ownerOrMaintainerName = 'maintainer'
+
+
+class DistributionPublisherConfigView(LaunchpadFormView):
+    """View class for configuring publisher options for a DistroSeries.
+
+    It redirects to the main distroseries page after a successful edit.
+    """
+    schema = IPublisherConfig
+    field_names = ['root_dir', 'base_url', 'copy_base_url']
+
+    @property
+    def label(self):
+        """See `LaunchpadFormView`."""
+        return 'Publisher configuration for %s' % self.context.title
+
+    @property
+    def page_title(self):
+        """The page title."""
+        return self.label
+
+    @property
+    def cancel_url(self):
+        """See `LaunchpadFormView`."""
+        return canonical_url(self.context)
+
+    @property
+    def initial_values(self):
+        """If the config already exists, set up the fields with data."""
+        config = getUtility(
+            IPublisherConfigSet).getByDistribution(self.context)
+        values = {}
+        if config is not None:
+            for name in self.field_names:
+                values[name] = getattr(config, name)
+
+        return values
+
+    @action("Save")
+    def save_action(self, action, data):
+        """Update the context and redirect to its overview page."""
+        config = getUtility(IPublisherConfigSet).getByDistribution(
+            self.context)
+        if config is None:
+            config = getUtility(IPublisherConfigSet).new(
+                distribution=self.context,
+                root_dir=data['root_dir'],
+                base_url=data['base_url'],
+                copy_base_url=data['copy_base_url'])
+        else:
+            form.applyChanges(config, self.form_fields, data, self.adapters)
+
+        self.request.response.addInfoNotification(
+            'Your changes have been applied.')
+        self.next_url = canonical_url(self.context)

=== modified file 'lib/lp/registry/browser/distroseries.py'
--- lib/lp/registry/browser/distroseries.py	2011-03-08 11:56:40 +0000
+++ lib/lp/registry/browser/distroseries.py	2011-03-23 13:31:30 +0000
@@ -24,7 +24,6 @@
 from zope.interface import Interface
 from zope.lifecycleevent import ObjectCreatedEvent
 from zope.schema import (
-    Bool,
     Choice,
     List,
     TextLine,
@@ -69,6 +68,7 @@
 from lp.app.widgets.itemswidgets import (
     LabeledMultiCheckBoxWidget,
     LaunchpadDropdownWidget,
+    LaunchpadRadioWidget,
     )
 from lp.blueprints.browser.specificationtarget import (
     HasSpecificationsMenuMixin,
@@ -184,7 +184,7 @@
 
     usedfor = IDistroSeries
     facet = 'overview'
-    links = ['edit', 'reassign', 'driver', 'answers',
+    links = ['edit', 'driver', 'answers',
              'packaging', 'needs_packaging', 'builds', 'queue',
              'add_port', 'create_milestone', 'subscribe', 'admin']
 
@@ -199,11 +199,6 @@
         summary = 'Someone with permission to set goals for this series'
         return Link('+driver', text, summary, icon='edit')
 
-    @enabled_with_permission('launchpad.Admin')
-    def reassign(self):
-        text = 'Change registrant'
-        return Link('+reassign', text, icon='edit')
-
     @enabled_with_permission('launchpad.Edit')
     def create_milestone(self):
         text = 'Create milestone'
@@ -496,9 +491,9 @@
     @action(_('Create Series'), name='create')
     def createAndAdd(self, action, data):
         """Create and add a new Distribution Series"""
-        owner = getUtility(ILaunchBag).user
+        registrant = getUtility(ILaunchBag).user
 
-        assert owner is not None
+        assert registrant is not None
         distroseries = self.context.newSeries(
             name=data['name'],
             displayname=data['displayname'],
@@ -507,7 +502,7 @@
             description=data['description'],
             version=data['version'],
             parent_series=data['parent_series'],
-            owner=owner)
+            registrant=registrant)
         notify(ObjectCreatedEvent(distroseries))
         self.next_url = canonical_url(distroseries)
         return distroseries
@@ -532,6 +527,27 @@
         return navigator
 
 
+# A simple vocabulary for package filtering on the source package
+# differences page
+NON_BLACKLISTED = 'non-blacklisted'
+BLACKLISTED = 'blacklisted'
+HIGHER_VERSION_THAN_PARENT = 'higher-than-parent'
+
+DEFAULT_PACKAGE_TYPE = NON_BLACKLISTED
+
+
+def make_package_type_vocabulary(parent_name):
+    return SimpleVocabulary((
+        SimpleTerm(
+            NON_BLACKLISTED, NON_BLACKLISTED, 'Non blacklisted packages'),
+        SimpleTerm(BLACKLISTED, BLACKLISTED, 'Blacklisted packages'),
+        SimpleTerm(
+            HIGHER_VERSION_THAN_PARENT,
+            HIGHER_VERSION_THAN_PARENT,
+            "Blacklisted packages with a higher version than in '%s'"
+                % parent_name)))
+
+
 class DistroSeriesNeedsPackagesView(LaunchpadView):
     """A View to show series package to upstream package relationships."""
 
@@ -551,10 +567,6 @@
     name_filter = TextLine(
         title=_("Package name contains"), required=False)
 
-    include_blacklisted_filter = Bool(
-        title=_("include blacklisted packages"),
-        required=False, default=False)
-
     selected_differences = List(
         title=_('Selected differences'),
         value_type=Choice(vocabulary=SimpleVocabulary([])),
@@ -567,6 +579,7 @@
     schema = IDifferencesFormSchema
     field_names = ['selected_differences']
     custom_widget('selected_differences', LabeledMultiCheckBoxWidget)
+    custom_widget('package_type', LaunchpadRadioWidget)
 
     page_title = 'Local package differences'
 
@@ -582,6 +595,7 @@
                 self.context.parent_series.displayname,
                 self.context.displayname,
                 ))
+
         super(DistroSeriesLocalDifferences, self).initialize()
 
     @property
@@ -593,6 +607,14 @@
                 self.context.parent_series.displayname,
                 ))
 
+    def setupPackageFilterRadio(self):
+        return form.Fields(Choice(
+            __name__='package_type',
+            vocabulary=make_package_type_vocabulary(
+                self.context.parent_series.displayname),
+            default=DEFAULT_PACKAGE_TYPE,
+            required=True))
+
     def setUpFields(self):
         """Add the selected differences field.
 
@@ -600,6 +622,10 @@
         for its own vocabulary, we set it up after all the others.
         """
         super(DistroSeriesLocalDifferences, self).setUpFields()
+        self.form_fields = (
+            self.setupPackageFilterRadio() +
+            self.form_fields)
+
         has_edit = check_permission('launchpad.Edit', self.context)
 
         terms = [
@@ -662,33 +688,41 @@
             return None
 
     @property
-    def specified_include_blacklisted_filter(self):
-        """If specified, return the 'blacklisted' filter from the GET form
+    def specified_package_type(self):
+        """If specified, return the package type filter from the GET form
         data.
         """
-        include_blacklisted_filter = self.request.query_string_params.get(
-            'field.include_blacklisted_filter')
-
-        if include_blacklisted_filter and include_blacklisted_filter[0]:
-            return include_blacklisted_filter[0]
+        package_type = self.request.query_string_params.get(
+            'field.package_type')
+        if package_type and package_type[0]:
+            return package_type[0]
         else:
-            return None
+            return DEFAULT_PACKAGE_TYPE
 
     @cachedproperty
     def cached_differences(self):
         """Return a batch navigator of filtered results."""
-        if self.specified_include_blacklisted_filter:
-            status=(
-                DistroSeriesDifferenceStatus.NEEDS_ATTENTION,
-                DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
-        else:
+        if self.specified_package_type == NON_BLACKLISTED:
             status=(
                 DistroSeriesDifferenceStatus.NEEDS_ATTENTION,)
+            child_version_higher = False
+        elif self.specified_package_type == BLACKLISTED:
+            status=(
+                DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
+            child_version_higher = False
+        elif self.specified_package_type == HIGHER_VERSION_THAN_PARENT:
+            status=(
+                DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
+            child_version_higher = True
+        else:
+            raise AssertionError('specified_package_type unknown')
+
         differences = getUtility(
             IDistroSeriesDifferenceSource).getForDistroSeries(
                 self.context,
                 source_package_name_filter=self.specified_name_filter,
-                status=status)
+                status=status,
+                child_version_higher=child_version_higher)
         return BatchNavigator(differences, self.request)
 
     @cachedproperty

=== added file 'lib/lp/registry/browser/tests/test_distribution_views.py'
--- lib/lp/registry/browser/tests/test_distribution_views.py	1970-01-01 00:00:00 +0000
+++ lib/lp/registry/browser/tests/test_distribution_views.py	2011-03-23 13:31:30 +0000
@@ -0,0 +1,167 @@
+# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+__metaclass__ = type
+
+import soupmatchers
+from zope.component import getUtility
+
+from canonical.launchpad.ftests import login
+from canonical.launchpad.webapp.servers import LaunchpadTestRequest
+from canonical.testing.layers import DatabaseFunctionalLayer
+from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
+from lp.registry.browser.distribution import DistributionPublisherConfigView
+from lp.registry.interfaces.distribution import IDistributionSet
+from lp.testing import (
+    login_celebrity,
+    TestCaseWithFactory,
+    )
+from lp.testing.sampledata import LAUNCHPAD_ADMIN
+from lp.testing.views import create_initialized_view
+
+
+class TestDistributionPublisherConfigView(TestCaseWithFactory):
+    """Test `DistributionPublisherConfigView`."""
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        # Create a test distribution.
+        super(TestDistributionPublisherConfigView, self).setUp()
+        self.distro = self.factory.makeDistribution(no_pubconf=True)
+        login(LAUNCHPAD_ADMIN)
+
+        self.ROOT_DIR = u"rootdir/test"
+        self.BASE_URL = u"http://base.url";
+        self.COPY_BASE_URL = u"http://copybase.url";
+
+    def test_empty_initial_values(self):
+        # Test that the page will display empty field values with no
+        # existing config set up.
+        view = DistributionPublisherConfigView(
+            self.distro, LaunchpadTestRequest())
+
+        for value in view.initial_values:
+            self.assertEqual(u"", value)
+
+    def test_previous_initial_values(self):
+        # Test that the initial values are the same as the ones in the
+        # existing database record.
+        pubconf = self.factory.makePublisherConfig(
+            distribution=self.distro)
+
+        view = DistributionPublisherConfigView(
+            self.distro, LaunchpadTestRequest())
+
+        self.assertEqual(pubconf.root_dir, view.initial_values["root_dir"])
+        self.assertEqual(pubconf.base_url, view.initial_values["base_url"])
+        self.assertEqual(
+            pubconf.copy_base_url, view.initial_values["copy_base_url"])
+
+    def _change_and_test_config(self):
+        form = {
+            'field.actions.save': 'save',
+            'field.root_dir': self.ROOT_DIR,
+            'field.base_url': self.BASE_URL,
+            'field.copy_base_url': self.COPY_BASE_URL,
+        }
+
+        view = DistributionPublisherConfigView(
+            self.distro, LaunchpadTestRequest(method='POST', form=form))
+        view.initialize()
+
+        config = getUtility(
+            IPublisherConfigSet).getByDistribution(self.distro)
+
+        self.assertEqual(self.ROOT_DIR, config.root_dir)
+        self.assertEqual(self.BASE_URL, config.base_url)
+        self.assertEqual(self.COPY_BASE_URL, config.copy_base_url)
+
+    def test_add_new_config(self):
+        # Test POSTing a new config.
+        self._change_and_test_config()
+
+    def test_change_existing_config(self):
+        # Test POSTing to change existing config.
+        pubconf = self.factory.makePublisherConfig(
+            distribution=self.distro,
+            root_dir=u"random",
+            base_url=u"blah",
+            copy_base_url=u"foo",
+            )
+        self._change_and_test_config()
+
+
+class TestDistroAddView(TestCaseWithFactory):
+    """Test the +add page for a new distribution."""
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(TestDistroAddView, self).setUp()
+        self.owner = self.factory.makePerson()
+        self.registrant = self.factory.makePerson()
+        self.simple_user = self.factory.makePerson()
+
+    def test_registrant_set_by_creation(self):
+        # The registrant field should be set to the Person creating
+        # the distribution.
+        admin = login_celebrity('admin')
+        distributionset = getUtility(IDistributionSet)
+        creation_form = {
+            'field.name': 'newbuntu',
+            'field.displayname': 'newbuntu',
+            'field.title': 'newbuntu',
+            'field.summary': 'newbuntu',
+            'field.description': 'newbuntu',
+            'field.domainname': 'newbuntu',
+            'field.members': self.simple_user.name,
+            'field.actions.save': 'Save',
+            }
+        view = create_initialized_view(
+            distributionset, '+add', principal=admin,
+            method='POST', form=creation_form)
+        distribution = distributionset.getByName('newbuntu')
+        self.assertEqual(distribution.owner, admin)
+        self.assertEqual(distribution.registrant, admin)
+
+
+class TestDistroReassignView(TestCaseWithFactory):
+    """Test the +reassign page for a new distribution."""
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(TestDistroReassignView, self).setUp()
+        self.owner = self.factory.makePerson()
+        self.registrant = self.factory.makePerson()
+        self.simple_user = self.factory.makePerson()
+
+    def test_reassign_distro_change_owner_not_registrant(self):
+        # Reassigning a distribution should not change the registrant.
+        admin = login_celebrity('admin')
+        distribution = self.factory.makeDistribution(
+            name="boobuntu", owner=self.owner, registrant=self.registrant)
+        reassign_form = {
+            'field.owner': self.simple_user.name,
+            'field.existing': 'existing',
+            'field.actions.change': 'Change',
+            }
+        view = create_initialized_view(
+            distribution, '+reassign', principal=admin,
+            method='POST', form=reassign_form)
+        self.assertEqual(distribution.owner, self.simple_user)
+        self.assertEqual(distribution.registrant, self.registrant)
+
+    def test_reassign_distro_page_title(self):
+        # Reassign should say maintainer instead of owner.
+        admin = login_celebrity('admin')
+        distribution = self.factory.makeDistribution(
+            name="boobuntu", owner=self.owner, registrant=self.registrant)
+        view = create_initialized_view(
+            distribution, '+reassign', principal=admin, method='GET')
+        header_match = soupmatchers.HTMLContains(
+            soupmatchers.Tag(
+                'Header should say maintainer (not owner)', 'h1',
+                text='Change the maintainer of Boobuntu'))
+        self.assertThat(view.render(), header_match)

=== modified file 'lib/lp/registry/browser/tests/test_series_views.py'
--- lib/lp/registry/browser/tests/test_series_views.py	2011-03-21 09:15:49 +0000
+++ lib/lp/registry/browser/tests/test_series_views.py	2011-03-23 13:31:30 +0000
@@ -3,13 +3,14 @@
 
 __metaclass__ = type
 
+import unittest
+
 from BeautifulSoup import BeautifulSoup
+import soupmatchers
 from storm.zope.interfaces import IResultSet
 from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
-import unittest
-
 from canonical.config import config
 from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
 from canonical.launchpad.testing.pages import find_tag_by_id
@@ -17,26 +18,31 @@
 from canonical.launchpad.webapp.publisher import canonical_url
 from canonical.testing.layers import (
     DatabaseFunctionalLayer,
+    LaunchpadFunctionalLayer,
     LaunchpadZopelessLayer,
-    LaunchpadFunctionalLayer,
+    )
+from lp.registry.browser.distroseries import (
+    BLACKLISTED,
+    HIGHER_VERSION_THAN_PARENT,
+    NON_BLACKLISTED,
     )
 from lp.registry.enum import (
     DistroSeriesDifferenceStatus,
     DistroSeriesDifferenceType,
     )
+from lp.services.features import (
+    getFeatureFlag,
+    install_feature_controller,
+    )
 from lp.services.features.flags import FeatureController
 from lp.services.features.model import (
     FeatureFlag,
     getFeatureStore,
     )
-from lp.services.features import (
-    getFeatureFlag,
-    install_feature_controller,
-    )
 from lp.testing import (
-    TestCaseWithFactory,
     login_person,
     person_logged_in,
+    TestCaseWithFactory,
     )
 from lp.testing.views import create_initialized_view
 
@@ -267,6 +273,28 @@
 
     layer = LaunchpadFunctionalLayer
 
+    def test_higher_radio_mentions_parent(self):
+        set_derived_series_ui_feature_flag(self)
+        parent_series = self.factory.makeDistroSeries(
+            name='lucid', displayname='Lucid')
+        derived_series = self.factory.makeDistroSeries(
+            name='derilucid', parent_series=parent_series)
+        diff1 = self.factory.makeDistroSeriesDifference(
+            derived_series=derived_series,
+            source_package_name_str="my-src-package")
+        view = create_initialized_view(
+            derived_series,
+            '+localpackagediffs')
+
+        radio_title = \
+            "&nbsp;Blacklisted packages with a higher version than in 'Lucid'"
+        radio_option_matches = soupmatchers.HTMLContains(
+            soupmatchers.Tag(
+                "radio displays parent's name", 'label',
+                text=radio_title),
+            )
+        self.assertThat(view.render(), radio_option_matches)
+
     def test_batch_filtered(self):
         # The name_filter parameter allows to filter packages by name.
         set_derived_series_ui_feature_flag(self)
@@ -293,9 +321,38 @@
         self.assertContentEqual(
             [diff2, diff1], unfiltered_view.cached_differences.batch)
 
-    def test_batch_blacklisted(self):
-        # The include_blacklisted_filter parameter allows to list
-        # blacklisted packages.
+    def test_batch_non_blacklisted(self):
+        # The default filter is all non blacklisted differences.
+        set_derived_series_ui_feature_flag(self)
+        derived_series = self.factory.makeDistroSeries(
+            name='derilucid', parent_series=self.factory.makeDistroSeries(
+                name='lucid'))
+        diff1 = self.factory.makeDistroSeriesDifference(
+            derived_series=derived_series,
+            source_package_name_str="my-src-package")
+        diff2 = self.factory.makeDistroSeriesDifference(
+            derived_series=derived_series,
+            source_package_name_str="my-second-src-package")
+        blacklisted_diff = self.factory.makeDistroSeriesDifference(
+            derived_series=derived_series,
+            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT)
+
+        filtered_view = create_initialized_view(
+            derived_series,
+            '+localpackagediffs',
+            query_string='field.package_type=%s' % NON_BLACKLISTED)
+        filtered_view2 = create_initialized_view(
+            derived_series,
+            '+localpackagediffs')
+
+        self.assertContentEqual(
+            [diff2, diff1], filtered_view.cached_differences.batch)
+        self.assertContentEqual(
+            [diff2, diff1], filtered_view2.cached_differences.batch)
+
+    def test_batch_differences_packages(self):
+        # field.package_type parameter allows to list only
+        # blacklisted differences.
         set_derived_series_ui_feature_flag(self)
         derived_series = self.factory.makeDistroSeries(
             name='derilucid', parent_series=self.factory.makeDistroSeries(
@@ -307,7 +364,7 @@
         blacklisted_view = create_initialized_view(
             derived_series,
             '+localpackagediffs',
-            query_string='field.include_blacklisted_filter=on')
+            query_string='field.package_type=%s' % BLACKLISTED)
         unblacklisted_view = create_initialized_view(
             derived_series,
             '+localpackagediffs')
@@ -317,6 +374,36 @@
         self.assertContentEqual(
             [], unblacklisted_view.cached_differences.batch)
 
+    def test_batch_blacklisted_differences_with_higher_version(self):
+        # field.package_type parameter allows to list only
+        # blacklisted differences with a child's version higher than parent's.
+        set_derived_series_ui_feature_flag(self)
+        derived_series = self.factory.makeDistroSeries(
+            name='derilucid', parent_series=self.factory.makeDistroSeries(
+                name='lucid'))
+        blacklisted_diff_higher = self.factory.makeDistroSeriesDifference(
+            derived_series=derived_series,
+            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
+            versions={'base': '1.1', 'parent': '1.3', 'derived': '1.10'})
+        blacklisted_diff_not_higher = self.factory.makeDistroSeriesDifference(
+            derived_series=derived_series,
+            status=DistroSeriesDifferenceStatus.BLACKLISTED_CURRENT,
+            versions={'base': '1.1', 'parent': '1.12', 'derived': '1.10'})
+
+        blacklisted_view = create_initialized_view(
+            derived_series,
+            '+localpackagediffs',
+            query_string='field.package_type=%s' % HIGHER_VERSION_THAN_PARENT)
+        unblacklisted_view = create_initialized_view(
+            derived_series,
+            '+localpackagediffs')
+
+        self.assertContentEqual(
+            [blacklisted_diff_higher],
+            blacklisted_view.cached_differences.batch)
+        self.assertContentEqual(
+            [], unblacklisted_view.cached_differences.batch)
+
     def test_canPerformSync_non_editor(self):
         # Non-editors do not see options to sync.
         derived_series = self.factory.makeDistroSeries(

=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml	2011-03-17 18:23:36 +0000
+++ lib/lp/registry/configure.zcml	2011-03-23 13:31:30 +0000
@@ -215,7 +215,7 @@
 
         <require
             permission="launchpad.Moderate"
-            set_attributes="version name status owner nominatedarchindep                                                                                                 changeslist datereleased"/>
+            set_attributes="version name status nominatedarchindep changeslist datereleased"/>
 
         <!-- IStructuralSubscriptionTarget -->
 
@@ -1532,6 +1532,7 @@
                 addAnswerContact
                 removeAnswerContact"/>
 
+
         <!-- IFAQTarget -->
 
         <allow
@@ -1575,6 +1576,12 @@
         for="lp.registry.interfaces.distribution.IDistributionSet"
         factory="lp.registry.browser.distribution.DistributionSetBreadcrumb"
         permission="zope.Public"/>
+    <adapter
+        provides="lp.archivepublisher.interfaces.publisherconfig.IPublisherConfig"
+        for="lp.registry.interfaces.distribution.IDistribution"
+        factory="lp.registry.adapters.distribution_to_publisherconfig"
+        permission="zope.Public"/>
+
 
     <!-- DistributionSet -->
 

=== modified file 'lib/lp/registry/doc/distroseries.txt'
--- lib/lp/registry/doc/distroseries.txt	2010-11-02 05:48:54 +0000
+++ lib/lp/registry/doc/distroseries.txt	2011-03-23 13:31:30 +0000
@@ -842,9 +842,11 @@
     >>> yo_series = youbuntu.newSeries(
     ...     name='island', displayname='Island', title='YouBuntu Island',
     ...     summary='summary', description='description', version='09.07',
-    ...     parent_series=warty, owner=yo_driver)
+    ...     parent_series=warty, registrant=yo_driver)
     >>> print yo_series.name
     island
+    >>> print yo_series.registrant.name
+    yo-driver
     >>> print yo_series.driver.name
     yo-driver
 
@@ -856,7 +858,7 @@
     >>> yo_series = youbuntu.newSeries(
     ...     name='forest', displayname='Forest', title='YouBuntu Forest',
     ...     summary='summary', description='description', version='09.07',
-    ...     parent_series=warty, owner=youbuntu.owner)
+    ...     parent_series=warty, registrant=youbuntu.owner)
     >>> print yo_series.name
     forest
     >>> print yo_series.driver
@@ -884,9 +886,11 @@
     >>> u_series = ubuntu.newSeries(
     ...     name='finch', displayname='Finch', title='Ubuntu Finch',
     ...     summary='summary', description='description', version='9.06',
-    ...     parent_series=warty, owner=ubuntu.owner)
+    ...     parent_series=warty, registrant=ubuntu.owner)
     >>> print u_series.name
     finch
+    >>> print u_series.registrant.name
+    ubuntu-team
     >>> print u_series.driver
     None
 
@@ -1056,7 +1060,7 @@
     mark
     >>> print sid.driver
     None
-    >>> print sid.owner.name
+    >>> print sid.registrant.name
     jdub
 
     >>> for d in sid.drivers:

=== modified file 'lib/lp/registry/interfaces/distribution.py'
--- lib/lp/registry/interfaces/distribution.py	2011-02-24 15:30:54 +0000
+++ lib/lp/registry/interfaces/distribution.py	2011-03-23 13:31:30 +0000
@@ -123,7 +123,7 @@
     """IDistribution properties requiring launchpad.Driver permission."""
 
     def newSeries(name, displayname, title, summary, description,
-                  version, parent_series, owner):
+                  version, parent_series, registrant):
         """Creates a new distroseries."""
 
 
@@ -209,6 +209,11 @@
         PublicPersonChoice(
             title=_("Owner"), vocabulary='ValidOwner',
             description=_("The distro's owner."), required=True))
+    registrant = exported(
+        PublicPersonChoice(
+            title=_("Registrant"), vocabulary='ValidPersonOrTeam',
+            description=_("The distro's registrant."), required=True,
+            readonly=True))
     date_created = exported(
         Datetime(title=_('Date created'),
                  description=_("The date this distribution was registered.")))
@@ -689,7 +694,7 @@
         """Return the IDistribution with the given name or None."""
 
     def new(name, displayname, title, description, summary, domainname,
-            members, owner, mugshot=None, logo=None, icon=None):
+            members, owner, registrant, mugshot=None, logo=None, icon=None):
         """Create a new distribution."""
 
     def getCurrentSourceReleases(distro_to_source_packagenames):

=== modified file 'lib/lp/registry/interfaces/distroseries.py'
--- lib/lp/registry/interfaces/distroseries.py	2011-03-10 14:05:51 +0000
+++ lib/lp/registry/interfaces/distroseries.py	2011-03-23 13:31:30 +0000
@@ -241,8 +241,13 @@
             description=_("The series from which this one was branched."),
             required=True, schema=Interface, # Really IDistroSeries, see below
             vocabulary='DistroSeries'))
-    owner = exported(
-        PublicPersonChoice(title=_("Owner"), vocabulary='ValidOwner'))
+    registrant = exported(
+        PublicPersonChoice(
+            title=_("Registrant"), vocabulary='ValidPersonOrTeam'))
+    owner = exported(Reference(
+        IPerson, title=_("Owning team of the derived series"), readonly=True,
+        description=_(
+            "This attribute mirrors the owner of the distribution.")))
     date_created = exported(
         Datetime(title=_("The date this series was registered.")))
     driver = exported(

=== modified file 'lib/lp/registry/interfaces/distroseriesdifference.py'
--- lib/lp/registry/interfaces/distroseriesdifference.py	2011-03-15 18:38:51 +0000
+++ lib/lp/registry/interfaces/distroseriesdifference.py	2011-03-23 13:31:30 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Interface classes for a difference between two distribution series."""
@@ -38,8 +38,8 @@
     )
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.person import IPerson
+from lp.registry.interfaces.role import IHasOwner
 from lp.registry.interfaces.sourcepackagename import ISourcePackageName
-from lp.registry.interfaces.role import IHasOwner
 from lp.soyuz.interfaces.packagediff import IPackageDiff
 from lp.soyuz.interfaces.publishing import ISourcePackagePublishingHistory
 
@@ -224,7 +224,8 @@
         distro_series,
         difference_type=DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
         source_package_name_filter=None,
-        status=None):
+        status=None,
+        child_version_higher=False):
         """Return differences for the derived distro series.
 
         :param distro_series: The derived distribution series which is to be
@@ -238,6 +239,9 @@
         :param status: Only differences matching the status(es) will be
             included.
         :type status: `DistroSeriesDifferenceStatus`.
+        :param child_version_higher: Only differences for which the child's
+            version is higher than the parent's version will be included.
+        :type child_version_higher: bool.
         :return: A result set of differences.
         """
 

=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py	2011-03-01 05:05:26 +0000
+++ lib/lp/registry/model/distribution.py	2011-03-23 13:31:30 +0000
@@ -243,6 +243,9 @@
     owner = ForeignKey(
         dbName='owner', foreignKey='Person',
         storm_validator=validate_public_person, notNull=True)
+    registrant = ForeignKey(
+        dbName='registrant', foreignKey='Person',
+        storm_validator=validate_public_person, notNull=True)
     bug_supervisor = ForeignKey(
         dbName='bug_supervisor', foreignKey='Person',
         storm_validator=validate_person,
@@ -1776,7 +1779,7 @@
         return user.inTeam(self.owner) or user.inTeam(admins)
 
     def newSeries(self, name, displayname, title, summary,
-                  description, version, parent_series, owner):
+                  description, version, parent_series, registrant):
         """See `IDistribution`."""
         series = DistroSeries(
             distribution=self,
@@ -1788,10 +1791,11 @@
             version=version,
             status=SeriesStatus.EXPERIMENTAL,
             parent_series=parent_series,
-            owner=owner)
-        if owner.inTeam(self.driver) and not owner.inTeam(self.owner):
+            registrant=registrant)
+        if (registrant.inTeam(self.driver)
+            and not registrant.inTeam(self.owner)):
             # This driver is a release manager.
-            series.driver = owner
+            series.driver = registrant
 
         # May wish to add this to the series rather than clearing the cache --
         # RBC 20100816.
@@ -1882,7 +1886,7 @@
         return pillar
 
     def new(self, name, displayname, title, description, summary, domainname,
-            members, owner, mugshot=None, logo=None, icon=None):
+            members, owner, registrant, mugshot=None, logo=None, icon=None):
         """See `IDistributionSet`."""
         distro = Distribution(
             name=name,
@@ -1894,6 +1898,7 @@
             members=members,
             mirror_admin=owner,
             owner=owner,
+            registrant=registrant,
             mugshot=mugshot,
             logo=logo,
             icon=icon)

=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py	2011-03-23 05:02:39 +0000
+++ lib/lp/registry/model/distroseries.py	2011-03-23 13:31:30 +0000
@@ -227,8 +227,8 @@
     datereleased = UtcDateTimeCol(notNull=False, default=None)
     parent_series = ForeignKey(
         dbName='parent_series', foreignKey='DistroSeries', notNull=False)
-    owner = ForeignKey(
-        dbName='owner', foreignKey='Person',
+    registrant = ForeignKey(
+        dbName='registrant', foreignKey='Person',
         storm_validator=validate_public_person, notNull=True)
     driver = ForeignKey(
         dbName="driver", foreignKey="Person",
@@ -406,6 +406,11 @@
         return self.distribution
 
     @property
+    def owner(self):
+        """See `IDistroSeries`."""
+        return self.distribution.owner
+
+    @property
     def sortkey(self):
         """A string to be used for sorting distro seriess.
 
@@ -1967,7 +1972,7 @@
             child = distribution.newSeries(
                 name=name, displayname=displayname, title=title,
                 summary=summary, description=description,
-                version=version, parent_series=self, owner=user)
+                version=version, parent_series=self, registrant=user)
             IStore(self).add(child)
         else:
             if child.parent_series is not self:

=== modified file 'lib/lp/registry/model/distroseriesdifference.py'
--- lib/lp/registry/model/distroseriesdifference.py	2011-03-15 18:38:51 +0000
+++ lib/lp/registry/model/distroseriesdifference.py	2011-03-23 13:31:30 +0000
@@ -11,14 +11,13 @@
 
 from debian.changelog import Changelog
 from lazr.enum import DBItem
+from sqlobject import StringCol
 from storm.expr import Desc
-
 from storm.locals import (
     And,
     Int,
     Reference,
     Storm,
-    Unicode,
     )
 from zope.component import getUtility
 from zope.interface import (
@@ -88,10 +87,10 @@
                     enum=DistroSeriesDifferenceStatus)
     difference_type = DBEnum(name='difference_type', allow_none=False,
                              enum=DistroSeriesDifferenceType)
-    source_version = Unicode(name='source_version', allow_none=True)
-    parent_source_version = Unicode(name='parent_source_version',
-                                    allow_none=True)
-    base_version = Unicode(name='base_version', allow_none=True)
+    source_version = StringCol(dbName='source_version', notNull=False)
+    parent_source_version = StringCol(dbName='parent_source_version',
+                                      notNull=False)
+    base_version = StringCol(dbName='base_version', notNull=False)
 
     @staticmethod
     def new(derived_series, source_package_name):
@@ -117,7 +116,8 @@
         distro_series,
         difference_type=DistroSeriesDifferenceType.DIFFERENT_VERSIONS,
         source_package_name_filter=None,
-        status=None):
+        status=None,
+        child_version_higher=False):
         """See `IDistroSeriesDifferenceSource`."""
         if status is None:
             status = (
@@ -131,12 +131,18 @@
             DistroSeriesDifference.difference_type == difference_type,
             DistroSeriesDifference.status.is_in(status),
         ]
+
         if source_package_name_filter:
             conditions.extend([
                 DistroSeriesDifference.source_package_name ==
                     SourcePackageName.id,
                 SourcePackageName.name == source_package_name_filter])
 
+        if child_version_higher:
+            conditions.extend([
+                DistroSeriesDifference.source_version >
+                    DistroSeriesDifference.parent_source_version])
+
         return IStore(DistroSeriesDifference).find(
             DistroSeriesDifference,
             And(*conditions))

=== modified file 'lib/lp/registry/stories/distribution/xx-distribution-launchpad-usage.txt'
--- lib/lp/registry/stories/distribution/xx-distribution-launchpad-usage.txt	2010-11-30 15:11:48 +0000
+++ lib/lp/registry/stories/distribution/xx-distribution-launchpad-usage.txt	2011-03-23 13:31:30 +0000
@@ -12,6 +12,10 @@
     Traceback (most recent call last):
       ...
     LinkNotFound...
+    >>> user_browser.getLink('Configure publisher')
+    Traceback (most recent call last):
+      ...
+    LinkNotFound...
     >>> user_browser.open('http://launchpad.dev/ubuntu/+edit')
     Traceback (most recent call last):
       ...
@@ -62,12 +66,31 @@
     >>> admin_browser.getControl(name='field.official_malone').value = False
     >>> admin_browser.getControl(name='field.blueprints_usage').value = (
     ... ['UNKNOWN'])
-    >>> admin_browser.getControl(name='field.answers_usage').value = ['UNKNOWN']
+    >>> admin_browser.getControl(
+    ...     name='field.answers_usage').value = ['UNKNOWN']
     >>> admin_browser.getControl('Change', index=3).click()
 
     >>> print admin_browser.url
     http://launchpad.dev/ubuntu
 
+Only administators can configure the publisher for the distribution:
+
+    >>> admin_browser.getLink('Configure publisher').click()
+    >>> print admin_browser.title
+    Publisher configuration for...
+
+    >>> admin_browser.getControl(
+    ...     name='field.root_dir').value = "/tmp/root_dir"
+    >>> admin_browser.getControl(
+    ...     name='field.base_url').value = "http://base.url/";
+    >>> admin_browser.getControl(
+    ...     name='field.copy_base_url').value = "http://copy.base.url/";
+    >>> admin_browser.getControl('Save').click()
+
+    >>> print admin_browser.url
+    http://launchpad.dev/ubuntu
+
+
 enable_bug_expiration and JavaScript
 ====================================
 

=== modified file 'lib/lp/registry/stories/distribution/xx-distribution-overview.txt'
--- lib/lp/registry/stories/distribution/xx-distribution-overview.txt	2010-05-17 20:09:03 +0000
+++ lib/lp/registry/stories/distribution/xx-distribution-overview.txt	2011-03-23 13:31:30 +0000
@@ -89,7 +89,9 @@
 
     >>> print extract_text(
     ...     find_tag_by_id(anon_browser.contents, 'registration'))
-    registered by Ubuntu Team on 2006-10-16
+    Registered by
+    Registry Administrators
+    on 2006-10-16
 
     >>> print extract_text(find_main_content(anon_browser.contents))
     Ubuntu Linux

=== modified file 'lib/lp/registry/stories/distroseries/xx-distroseries-index.txt'
--- lib/lp/registry/stories/distroseries/xx-distroseries-index.txt	2010-08-05 20:17:40 +0000
+++ lib/lp/registry/stories/distroseries/xx-distroseries-index.txt	2011-03-23 13:31:30 +0000
@@ -20,7 +20,9 @@
 
     >>> print extract_text(
     ...     find_tag_by_id(anon_browser.contents, 'registration'))
-    registered by Ubuntu Team on 2006-10-16
+    Registered by
+    Ubuntu Team on
+    2006-10-16
 
     >>> print extract_text(find_main_content(anon_browser.contents))
     Warty

=== removed file 'lib/lp/registry/stories/distroseries/xx-reassign-distroseries.txt'
--- lib/lp/registry/stories/distroseries/xx-reassign-distroseries.txt	2009-11-22 15:43:16 +0000
+++ lib/lp/registry/stories/distroseries/xx-reassign-distroseries.txt	1970-01-01 00:00:00 +0000
@@ -1,72 +0,0 @@
-  Change the owner of the grumpy distroseries.
-
-
-  Logged in as Sample Person we don't have permission to do that, because he's
-  not the owner nor a member of the admins team.
-
-  >>> print http(r"""
-  ... GET /ubuntu/grumpy/+reassign HTTP/1.1
-  ... Authorization: Basic test@xxxxxxxxxxxxx:test
-  ... """)
-  HTTP/1.1 403 Forbidden
-  Content-Length: ...
-  Content-Type: text/html;charset=utf-8
-  ...
-
-
-  Now we're logged in as Mark Shutleworth and reassign grumpy to Foo Bar.
-
-  >>> print http(r"""
-  ... POST /ubuntu/grumpy/+reassign HTTP/1.1
-  ... Authorization: Basic mark@xxxxxxxxxxx:test
-  ... field.owner=name16&field.existing=existing"""
-  ... r"""&field.actions.change=Change""")
-  HTTP/1.1 303 See Other
-  ...
-  Location: http://localhost/ubuntu/grumpy
-  ...
-
-
-  Foo Bar is the owner here
-
-  >>> print http(r"""
-  ... GET /ubuntu/grumpy/ HTTP/1.1
-  ... Authorization: Basic mark@xxxxxxxxxxx:test
-  ... """)
-  HTTP/1.1 200 Ok
-  Content-Length: ...
-  Content-Type: text/html;charset=utf-8
-  ...
-  ...registered by...
-  ...
-  ...Foo Bar...
-  ...
-
-
-  Now we reassign it to a newly create team: ubuntu2
-
-  >>> print http(r"""
-  ... POST /ubuntu/grumpy/+reassign HTTP/1.1
-  ... Authorization: Basic mark@xxxxxxxxxxx:test
-  ... field.owner=ubuntu2&field.existing=new&field.actions.change=Change""")
-  HTTP/1.1 303 See Other
-  ...
-  Location: http://localhost/ubuntu/grumpy
-  ...
-
-
-  And the new team can be seen as the owner.
-
-  >>> print http(r"""
-  ... GET /ubuntu/grumpy/ HTTP/1.1
-  ... Authorization: Basic mark@xxxxxxxxxxx:test
-  ... """)
-  HTTP/1.1 200 Ok
-  Content-Length: ...
-  Content-Type: text/html;charset=utf-8
-  ...
-  ...registered by...
-  ...
-  ...ubuntu2...
-  ...
-

=== modified file 'lib/lp/registry/stories/webservice/xx-distribution.txt'
--- lib/lp/registry/stories/webservice/xx-distribution.txt	2011-02-13 22:54:48 +0000
+++ lib/lp/registry/stories/webservice/xx-distribution.txt	2011-03-23 13:31:30 +0000
@@ -43,6 +43,7 @@
     name: u'ubuntu'
     official_bug_tags: []
     owner_link: u'http://.../~ubuntu-team'
+    registrant_link: u'http://.../~registry'
     resource_type_link: u'http://.../#distribution'
     security_contact_link: None
     self_link: u'http://.../ubuntu'

=== modified file 'lib/lp/registry/stories/webservice/xx-distroseries.txt'
--- lib/lp/registry/stories/webservice/xx-distroseries.txt	2011-01-26 19:35:17 +0000
+++ lib/lp/registry/stories/webservice/xx-distroseries.txt	2011-03-23 13:31:30 +0000
@@ -75,8 +75,9 @@
     main_archive_link: u'http://.../ubuntu/+archive/primary'
     name: u'hoary'
     official_bug_tags: []
-    owner_link: u'http://.../~mark'
+    owner_link: u'http://.../~ubuntu-team'
     parent_series_link: u'http://.../ubuntu/warty'
+    registrant_link: u'http://.../~mark'
     resource_type_link: ...
     self_link: u'http://.../ubuntu/hoary'
     status: u'Active Development'

=== modified file 'lib/lp/registry/templates/distribution-index.pt'
--- lib/lp/registry/templates/distribution-index.pt	2010-10-10 21:54:16 +0000
+++ lib/lp/registry/templates/distribution-index.pt	2011-03-23 13:31:30 +0000
@@ -11,8 +11,8 @@
     </tal:heading>
 
     <tal:registering metal:fill-slot="registering">
-        registered by
-        <a tal:replace="structure context/owner/fmt:link" />
+        Registered by
+        <a tal:replace="structure context/registrant/fmt:link" />
         <span tal:content="context/date_created/fmt:displaydate"
               tal:attributes="title context/date_created/fmt:datetime"
           >on 2005-01-01</span>

=== modified file 'lib/lp/registry/templates/distroseries-index.pt'
--- lib/lp/registry/templates/distroseries-index.pt	2010-10-10 21:54:16 +0000
+++ lib/lp/registry/templates/distroseries-index.pt	2011-03-23 13:31:30 +0000
@@ -19,8 +19,8 @@
     </tal:heading>
 
     <tal:registering metal:fill-slot="registering">
-        registered by
-        <a tal:replace="structure context/owner/fmt:link" />
+        Registered by
+        <a tal:replace="structure context/registrant/fmt:link" />
         <span tal:content="context/date_created/fmt:displaydate"
               tal:attributes="title context/date_created/fmt:datetime"
           >on 2005-01-01</span>
@@ -121,9 +121,6 @@
           <li tal:condition="overview_menu/admin/enabled">
             <a tal:replace="structure overview_menu/admin/fmt:link" />
           </li>
-          <li tal:condition="overview_menu/reassign/enabled">
-            <a tal:replace="structure overview_menu/reassign/fmt:link" />
-          </li>
           <li>
             <a tal:replace="structure overview_menu/subscribe/fmt:link" />
           </li>

=== modified file 'lib/lp/registry/templates/distroseries-localdifferences.pt'
--- lib/lp/registry/templates/distroseries-localdifferences.pt	2011-03-07 13:43:29 +0000
+++ lib/lp/registry/templates/distroseries-localdifferences.pt	2011-03-23 13:31:30 +0000
@@ -6,6 +6,15 @@
   metal:use-macro="view/macro:page/main_only"
   i18n:domain="launchpad">
   <body>
+
+    <metal:block fill-slot="head_epilogue">
+      <style type="text/css">
+        .distroseries-localdiff-search-filter input[type="radio"] {
+          margin-left: 0;
+        }
+      </style>
+    </metal:block>
+
     <tal:heading metal:fill-slot="heading">
       <h1 tal:content="view/label">Package differences between ...</h1>
     </tal:heading>

=== modified file 'lib/lp/registry/templates/packagesearch-macros.pt'
--- lib/lp/registry/templates/packagesearch-macros.pt	2011-03-09 23:03:28 +0000
+++ lib/lp/registry/templates/packagesearch-macros.pt	2011-03-23 13:31:30 +0000
@@ -71,12 +71,9 @@
         type="text" name="field.name_filter"
         tal:attributes="value request/field.name_filter|nothing"/>
       <input type="submit" value="Filter" />
-      <br />
-      <input
-        id="field.include_blacklisted_filter"
-        type="checkbox" name="field.include_blacklisted_filter"
-        tal:attributes="checked request/field.include_blacklisted_filter|nothing"/>
-      <label for="field.include_blacklisted_filter">include blacklisted packages</label>
+     <br />
+     <tal:package_type
+       replace="structure view/widgets/package_type" />
     </div>
     <div class="clearfix"></div>
   </form>

=== modified file 'lib/lp/registry/tests/test_distribution.py'
--- lib/lp/registry/tests/test_distribution.py	2011-03-14 12:22:17 +0000
+++ lib/lp/registry/tests/test_distribution.py	2011-03-23 13:31:30 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for Distribution."""
@@ -6,14 +6,22 @@
 __metaclass__ = type
 
 from lazr.lifecycle.snapshot import Snapshot
+import soupmatchers
+from testtools.matchers import (
+    MatchesAny,
+    Not,
+    )
+from zope.component import getUtility
 from zope.security.proxy import removeSecurityProxy
 
+from canonical.launchpad.webapp import canonical_url
 from canonical.testing.layers import (
     DatabaseFunctionalLayer,
     LaunchpadFunctionalLayer,
     )
 from lp.registry.errors import NoSuchDistroSeries
 from lp.registry.interfaces.distribution import IDistribution
+from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.series import SeriesStatus
 from lp.registry.tests.test_distroseries import (
     TestDistroSeriesCurrentSourceReleases,
@@ -22,7 +30,11 @@
 from lp.soyuz.interfaces.distributionsourcepackagerelease import (
     IDistributionSourcePackageRelease,
     )
-from lp.testing import TestCaseWithFactory
+from lp.testing import (
+    login_person,
+    TestCaseWithFactory,
+    )
+from lp.testing.views import create_initialized_view
 
 
 class TestDistribution(TestCaseWithFactory):
@@ -97,7 +109,7 @@
         distribution.newSeries(
             name='bar', displayname='Bar', title='Bar', summary='',
             description='', version='1', parent_series=None,
-            owner=self.factory.makePerson())
+            registrant=self.factory.makePerson())
         self.assertNotIn("series", cache)
 
         # New cached value.
@@ -174,3 +186,99 @@
             self.assertFalse(
                 hasattr(snapshot, attribute),
                 "Snapshot should not include %s." % attribute)
+
+
+class TestDistributionPage(TestCaseWithFactory):
+    """A TestCase for the distribution page."""
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(TestDistributionPage, self).setUp('foo.bar@xxxxxxxxxxxxx')
+        self.distro = self.factory.makeDistribution(
+            name="distro", displayname=u'distro')
+        self.admin = getUtility(IPersonSet).getByEmail(
+            'admin@xxxxxxxxxxxxx')
+        self.simple_user = self.factory.makePerson()
+
+    def test_distributionpage_addseries_link(self):
+        """ Verify that an admin sees the +addseries link."""
+        login_person(self.admin)
+        view = create_initialized_view(
+            self.distro, '+index', principal=self.admin)
+        series_matches = soupmatchers.HTMLContains(
+            soupmatchers.Tag(
+                'link to add a series', 'a',
+                attrs={'href':
+                    canonical_url(self.distro, view_name='+addseries')},
+                text='Add series'),
+            soupmatchers.Tag(
+                'Series and milestones widget', 'h2',
+                text='Series and milestones'),
+            )
+        self.assertThat(view.render(), series_matches)
+
+    def test_distributionpage_addseries_link_noadmin(self):
+        """Verify that a non-admin does not see the +addseries link
+        nor the series header (since there is no series yet).
+        """
+        login_person(self.simple_user)
+        view = create_initialized_view(
+            self.distro, '+index', principal=self.simple_user)
+        add_series_match = soupmatchers.HTMLContains(
+            soupmatchers.Tag(
+                'link to add a series', 'a',
+                attrs={'href':
+                    canonical_url(self.distro, view_name='+addseries')},
+                text='Add series'))
+        series_header_match = soupmatchers.HTMLContains(
+            soupmatchers.Tag(
+                'Series and milestones widget', 'h2',
+                text='Series and milestones'))
+        self.assertThat(
+            view.render(),
+            Not(MatchesAny(add_series_match, series_header_match)))
+
+    def test_distributionpage_series_list_noadmin(self):
+        """Verify that a non-admin does see the series list
+        when there is a series.
+        """
+        self.factory.makeDistroSeries(distribution=self.distro,
+            status=SeriesStatus.CURRENT)
+        login_person(self.simple_user)
+        view = create_initialized_view(
+            self.distro, '+index', principal=self.simple_user)
+        add_series_match = soupmatchers.HTMLContains(
+            soupmatchers.Tag(
+                'link to add a series', 'a',
+                attrs={'href':
+                    canonical_url(self.distro, view_name='+addseries')},
+                text='Add series'))
+        series_header_match = soupmatchers.HTMLContains(
+            soupmatchers.Tag(
+                'Series and milestones widget', 'h2',
+                text='Series and milestones'))
+        self.assertThat(view.render(), series_header_match)
+        self.assertThat(view.render(), Not(add_series_match))
+
+
+class DistroRegistrantTestCase(TestCaseWithFactory):
+    """A TestCase for registrants and owners of a distribution.
+
+    The registrant is the creator of the distribution (read-only field).
+    The owner is really the maintainer.
+    """
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(DistroRegistrantTestCase, self).setUp()
+        self.owner = self.factory.makePerson()
+        self.registrant = self.factory.makePerson()
+
+    def test_distro_registrant_owner_differ(self):
+        distribution = self.factory.makeDistribution(
+            name="boobuntu", owner=self.owner, registrant=self.registrant)
+        self.assertNotEqual(distribution.owner, distribution.registrant)
+        self.assertEqual(distribution.owner, self.owner)
+        self.assertEqual(distribution.registrant, self.registrant)

=== modified file 'lib/lp/registry/tests/test_distributionsourcepackage.py'
--- lib/lp/registry/tests/test_distributionsourcepackage.py	2010-10-04 19:50:45 +0000
+++ lib/lp/registry/tests/test_distributionsourcepackage.py	2011-03-23 13:31:30 +0000
@@ -32,7 +32,8 @@
         distribution = distribution_set.new(name='wart',
             displayname='wart', title='wart', description='lots of warts',
             summary='lots of warts', domainname='wart.dumb',
-            members=self.factory.makeTeam(), owner=self.factory.makePerson())
+            members=self.factory.makeTeam(), owner=self.factory.makePerson(),
+            registrant=self.factory.makePerson())
         naked_distribution = removeSecurityProxy(distribution)
         self.factory.makeSourcePackage(distroseries=distribution)
         dsp = naked_distribution.getSourcePackage(name='pmount')

=== modified file 'lib/lp/registry/tests/test_distroseries.py'
--- lib/lp/registry/tests/test_distroseries.py	2011-03-10 14:05:51 +0000
+++ lib/lp/registry/tests/test_distroseries.py	2011-03-23 13:31:30 +0000
@@ -211,6 +211,15 @@
         self.assertContentEqual(
             [distroseries], distroseries.parent_series.getDerivedSeries())
 
+    def test_registrant_owner_differ(self):
+        # The registrant is the creator whereas the owner is the distribution's
+        # owner
+        registrant = self.factory.makePerson()
+        distroseries = self.factory.makeDistroRelease(registrant=registrant)
+        self.assertEquals(distroseries.distribution.owner, distroseries.owner)
+        self.assertEquals(registrant, distroseries.registrant)
+        self.assertNotEqual(distroseries.registrant, distroseries.owner)
+
 
 class TestDistroSeriesPackaging(TestCaseWithFactory):
 

=== modified file 'lib/lp/registry/tests/test_pillarname_triggers.py'
--- lib/lp/registry/tests/test_pillarname_triggers.py	2010-10-04 19:50:45 +0000
+++ lib/lp/registry/tests/test_pillarname_triggers.py	2011-03-23 13:31:30 +0000
@@ -46,12 +46,12 @@
         # Inserting a new Distribution will populate PillarName
         cur.execute("""
             INSERT INTO Distribution (
-                name, description, domainname, owner, displayname,
-                summary, title, members, mirror_admin
+                name, description, domainname, owner, registrant,
+                displayname, summary, title, members, mirror_admin
                 )
                 VALUES (
-                    'whatever', 'whatever', 'whatever', 1, 'whatever',
-                    'whatever', 'whatever', 1, 1
+                    'whatever', 'whatever', 'whatever', 1, 1,
+                    'whatever', 'whatever', 'whatever', 1, 1
                     )
             """)
         self.failUnless(is_in_sync('whatever'))
@@ -69,7 +69,8 @@
             """)
         self.failUnless(is_in_sync('whatever2'))
 
-        # Deleting a Distribution removes the corresponding entry in PillarName
+        # Deleting a Distribution removes the corresponding entry in
+        # PillarName
         cur.execute("DELETE FROM Distribution WHERE name='whatever2'")
         cur.execute("SELECT COUNT(*) FROM PillarName WHERE name='whatever2'")
         self.failUnlessEqual(cur.fetchone()[0], 0)
@@ -101,7 +102,8 @@
 
         # Inserting a new Product will populate PillarName
         cur.execute("""
-            INSERT INTO Product (owner, registrant, name, displayname, title, summary)
+            INSERT INTO Product (
+                owner, registrant, name, displayname, title, summary)
             VALUES (
                 1, 1, 'whatever', 'whatever', 'whatever', 'whatever'
                 )
@@ -154,7 +156,8 @@
         # Inserting a new ProjectGroup will populate PillarName
         cur.execute("""
             INSERT INTO Project (
-                name, owner, registrant, displayname, title, summary, description
+                name, owner, registrant, displayname, title, summary,
+                description
                 )
                 VALUES (
                     'whatever', 1, 1, 'whatever', 'whatever',

=== modified file 'lib/lp/scripts/garbo.py'
--- lib/lp/scripts/garbo.py	2011-03-09 23:43:02 +0000
+++ lib/lp/scripts/garbo.py	2011-03-23 13:31:30 +0000
@@ -86,6 +86,7 @@
     LaunchpadCronScript,
     SilentLaunchpadScriptFailure,
     )
+from lp.services.session.model import SessionData
 from lp.soyuz.model.files import SourcePackageReleaseFile
 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
 from lp.translations.interfaces.potemplate import IPOTemplateSet
@@ -125,10 +126,14 @@
     # from. Must be overridden.
     target_table_class = None
 
-    # The column name in target_table we use as the integer key. May be
-    # overridden.
+    # The column name in target_table we use as the key. The type must
+    # match that returned by the ids_to_prune_query and the
+    # target_table_key_type. May be overridden.
     target_table_key = 'id'
 
+    # SQL type of the target_table_key. May be overridden.
+    target_table_key_type = 'integer'
+
     # An SQL query returning a list of ids to remove from target_table.
     # The query must return a single column named 'id' and should not
     # contain duplicates. Must be overridden.
@@ -137,10 +142,17 @@
     # See `TunableLoop`. May be overridden.
     maximum_chunk_size = 10000
 
+    def getStore(self):
+        """The master Store for the table we are pruning.
+
+        May be overridden.
+        """
+        return IMasterStore(self.target_table_class)
+
     def __init__(self, log, abort_time=None):
         super(BulkPruner, self).__init__(log, abort_time)
 
-        self.store = IMasterStore(self.target_table_class)
+        self.store = self.getStore()
         self.target_table_name = self.target_table_class.__storm_table__
 
         # Open the cursor.
@@ -157,11 +169,14 @@
     def __call__(self, chunk_size):
         """See `ITunableLoop`."""
         result = self.store.execute("""
-            DELETE FROM %s WHERE %s IN (
+            DELETE FROM %s
+            WHERE %s IN (
                 SELECT id FROM
-                cursor_fetch('bulkprunerid', %d) AS f(id integer))
+                cursor_fetch('bulkprunerid', %d) AS f(id %s))
             """
-            % (self.target_table_name, self.target_table_key, chunk_size))
+            % (
+                self.target_table_name, self.target_table_key,
+                chunk_size, self.target_table_key_type))
         self._num_removed = result.rowcount
         transaction.commit()
 
@@ -175,9 +190,7 @@
 
     XXX bug=723596 StuartBishop: This job only needs to run once per month.
     """
-
     target_table_class = POTranslation
-
     ids_to_prune_query = """
         SELECT POTranslation.id AS id FROM POTranslation
         EXCEPT (
@@ -204,6 +217,68 @@
         """
 
 
+class SessionPruner(BulkPruner):
+    """Base class for session removal."""
+
+    target_table_class = SessionData
+    target_table_key = 'client_id'
+    target_table_key_type = 'text'
+
+
+class AntiqueSessionPruner(SessionPruner):
+    """Remove sessions not accessed for 60 days"""
+
+    ids_to_prune_query = """
+        SELECT client_id AS id FROM SessionData
+        WHERE last_accessed < CURRENT_TIMESTAMP - CAST('60 days' AS interval)
+        """
+
+
+class UnusedSessionPruner(SessionPruner):
+    """Remove sessions older than 1 day with no authentication credentials."""
+
+    ids_to_prune_query = """
+        SELECT client_id AS id FROM SessionData
+        WHERE
+            last_accessed < CURRENT_TIMESTAMP - CAST('1 day' AS interval)
+            AND client_id NOT IN (
+                SELECT client_id
+                FROM SessionPkgData
+                WHERE
+                    product_id = 'launchpad.authenticateduser'
+                    AND key='logintime')
+        """
+
+
+class DuplicateSessionPruner(SessionPruner):
+    """Remove all but the most recent 6 authenticated sessions for a user.
+
+    We sometimes see users with dozens or thousands of authenticated
+    sessions. To limit exposure to replay attacks, we remove all but
+    the most recent 6 of them for a given user.
+    """
+
+    ids_to_prune_query = """
+        SELECT client_id AS id
+        FROM (
+            SELECT
+                sessiondata.client_id,
+                last_accessed,
+                rank() OVER pickle AS rank
+            FROM SessionData, SessionPkgData
+            WHERE
+                SessionData.client_id = SessionPkgData.client_id
+                AND product_id = 'launchpad.authenticateduser'
+                AND key='accountid'
+            WINDOW pickle AS (PARTITION BY pickle ORDER BY last_accessed DESC)
+            ) AS whatever
+        WHERE
+            rank > 6
+            AND last_accessed < CURRENT_TIMESTAMP AT TIME ZONE 'UTC'
+                - CAST('1 hour' AS interval)
+        """
+
+
 class OAuthNoncePruner(TunableLoop):
     """An ITunableLoop to prune old OAuthNonce records.
 
@@ -1113,6 +1188,9 @@
         RevisionCachePruner,
         BugHeatUpdater,
         BugWatchScheduler,
+        AntiqueSessionPruner,
+        UnusedSessionPruner,
+        DuplicateSessionPruner,
         PopulateSPRChangelogs,
         ]
     experimental_tunable_loops = []

=== modified file 'lib/lp/scripts/tests/test_garbo.py'
--- lib/lp/scripts/tests/test_garbo.py	2011-03-09 23:43:02 +0000
+++ lib/lp/scripts/tests/test_garbo.py	2011-03-23 13:31:30 +0000
@@ -20,7 +20,10 @@
     Min,
     SQL,
     )
-from storm.locals import Storm, Int
+from storm.locals import (
+    Int,
+    Storm,
+    )
 from storm.store import Store
 import transaction
 from zope.component import getUtility
@@ -74,13 +77,20 @@
     PersonCreationRationale,
     )
 from lp.scripts.garbo import (
+    AntiqueSessionPruner,
     BulkPruner,
     DailyDatabaseGarbageCollector,
+    DuplicateSessionPruner,
     HourlyDatabaseGarbageCollector,
     OpenIDConsumerAssociationPruner,
+    UnusedSessionPruner,
     )
 from lp.services.job.model.job import Job
 from lp.services.log.logger import BufferLogger
+from lp.services.session.model import (
+    SessionData,
+    SessionPkgData,
+    )
 from lp.soyuz.enums import PackagePublishingStatus
 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
 from lp.testing import (
@@ -195,6 +205,153 @@
             pruner(chunk_size)
 
 
+class TestSessionPruner(TestCase):
+    layer = ZopelessDatabaseLayer
+
+    def setUp(self):
+        super(TestCase, self).setUp()
+
+        # Session database isn't reset between tests. We need to do this
+        # manually.
+        nuke_all_sessions = IMasterStore(SessionData).find(SessionData).remove
+        nuke_all_sessions()
+        self.addCleanup(nuke_all_sessions)
+
+        recent = datetime.now(UTC)
+        yesterday = recent - timedelta(days=1)
+        ancient = recent - timedelta(days=61)
+
+        self.make_session(u'recent_auth', recent, 'auth1')
+        self.make_session(u'recent_unauth', recent, False)
+        self.make_session(u'yesterday_auth', yesterday, 'auth2')
+        self.make_session(u'yesterday_unauth', yesterday, False)
+        self.make_session(u'ancient_auth', ancient, 'auth3')
+        self.make_session(u'ancient_unauth', ancient, False)
+
+    def make_session(self, client_id, accessed, authenticated=None):
+        session_data = SessionData()
+        session_data.client_id = client_id
+        session_data.last_accessed = accessed
+        IMasterStore(SessionData).add(session_data)
+
+        if authenticated:
+            # Add login time information.
+            session_pkg_data = SessionPkgData()
+            session_pkg_data.client_id = client_id
+            session_pkg_data.product_id = u'launchpad.authenticateduser'
+            session_pkg_data.key = u'logintime'
+            session_pkg_data.pickle = 'value is ignored'
+            IMasterStore(SessionPkgData).add(session_pkg_data)
+
+            # Add authenticated as information.
+            session_pkg_data = SessionPkgData()
+            session_pkg_data.client_id = client_id
+            session_pkg_data.product_id = u'launchpad.authenticateduser'
+            session_pkg_data.key = u'accountid'
+            # Normally Account.id, but the session pruning works
+            # at the SQL level and doesn't unpickle anything.
+            session_pkg_data.pickle = authenticated
+            IMasterStore(SessionPkgData).add(session_pkg_data)
+
+    def sessionExists(self, client_id):
+        store = IMasterStore(SessionData)
+        return not store.find(
+            SessionData, SessionData.client_id == client_id).is_empty()
+
+    def test_antique_session_pruner(self):
+        chunk_size = 2
+        log = BufferLogger()
+        pruner = AntiqueSessionPruner(log)
+        try:
+            while not pruner.isDone():
+                pruner(chunk_size)
+        finally:
+            pruner.cleanUp()
+
+        expected_sessions = set([
+            u'recent_auth',
+            u'recent_unauth',
+            u'yesterday_auth',
+            u'yesterday_unauth',
+            # u'ancient_auth',
+            # u'ancient_unauth',
+            ])
+
+        found_sessions = set(
+            IMasterStore(SessionData).find(SessionData.client_id))
+
+        self.assertEqual(expected_sessions, found_sessions)
+
+    def test_unused_session_pruner(self):
+        chunk_size = 2
+        log = BufferLogger()
+        pruner = UnusedSessionPruner(log)
+        try:
+            while not pruner.isDone():
+                pruner(chunk_size)
+        finally:
+            pruner.cleanUp()
+
+        expected_sessions = set([
+            u'recent_auth',
+            u'recent_unauth',
+            u'yesterday_auth',
+            # u'yesterday_unauth',
+            u'ancient_auth',
+            # u'ancient_unauth',
+            ])
+
+        found_sessions = set(
+            IMasterStore(SessionData).find(SessionData.client_id))
+
+        self.assertEqual(expected_sessions, found_sessions)
+
+    def test_duplicate_session_pruner(self):
+        # None of the sessions created in setUp() are duplicates, so
+        # they will all survive the pruning.
+        expected_sessions = set([
+            u'recent_auth',
+            u'recent_unauth',
+            u'yesterday_auth',
+            u'yesterday_unauth',
+            u'ancient_auth',
+            u'ancient_unauth',
+            ])
+
+        now = datetime.now(UTC)
+
+        # Make some duplicate logins from a few days ago.
+        # Only the most recent 6 will be kept. Oldest is 'old dupe 9',
+        # most recent 'old dupe 1'.
+        for count in range(1, 10):
+            self.make_session(
+                u'old dupe %d' % count,
+                now - timedelta(days=2, seconds=count),
+                'old dupe')
+        for count in range(1, 7):
+            expected_sessions.add(u'old dupe %d' % count)
+
+        # Make some other duplicate logins less than an hour old.
+        # All of these will be kept.
+        for count in range(1, 10):
+            self.make_session(u'new dupe %d' % count, now, 'new dupe')
+            expected_sessions.add(u'new dupe %d' % count)
+
+        chunk_size = 2
+        log = BufferLogger()
+        pruner = DuplicateSessionPruner(log)
+        try:
+            while not pruner.isDone():
+                pruner(chunk_size)
+        finally:
+            pruner.cleanUp()
+
+        found_sessions = set(
+            IMasterStore(SessionData).find(SessionData.client_id))
+
+        self.assertEqual(expected_sessions, found_sessions)
+
+
 class TestGarbo(TestCaseWithFactory):
     layer = LaunchpadZopelessLayer
 
@@ -223,7 +380,7 @@
         return collector
 
     def test_OAuthNoncePruner(self):
-        now = datetime.utcnow().replace(tzinfo=UTC)
+        now = datetime.now(UTC)
         timestamps = [
             now - timedelta(days=2), # Garbage
             now - timedelta(days=1) - timedelta(seconds=60), # Garbage
@@ -301,7 +458,7 @@
         self.failUnless(earliest >= now - 24*60*60, 'Still have old nonces')
 
     def test_CodeImportResultPruner(self):
-        now = datetime.utcnow().replace(tzinfo=UTC)
+        now = datetime.now(UTC)
         store = IMasterStore(CodeImportResult)
 
         results_to_keep_count = (
@@ -358,7 +515,7 @@
             >= now - timedelta(days=30))
 
     def test_CodeImportEventPruner(self):
-        now = datetime.utcnow().replace(tzinfo=UTC)
+        now = datetime.now(UTC)
         store = IMasterStore(CodeImportResult)
 
         LaunchpadZopelessLayer.switchDbUser('testadmin')

=== modified file 'lib/lp/scripts/utilities/sanitizedb.py'
--- lib/lp/scripts/utilities/sanitizedb.py	2011-02-21 00:06:55 +0000
+++ lib/lp/scripts/utilities/sanitizedb.py	2011-03-23 13:31:30 +0000
@@ -13,7 +13,10 @@
 import subprocess
 import sys
 
-from storm.locals import Or
+from storm.expr import (
+    Join,
+    Or,
+    )
 import transaction
 from zope.component import getUtility
 
@@ -254,8 +257,14 @@
     def removePrivateBugMessages(self):
         """Remove all hidden bug messages."""
         from lp.bugs.model.bugmessage import BugMessage
+        from canonical.launchpad.database.message import Message
+        message_ids = list(self.store.using(*[
+            BugMessage,
+            Join(Message, BugMessage.messageID == Message.id),
+            ]).find(BugMessage.id, Message.visible == False))
+        self.store.flush()
         count = self.store.find(
-            BugMessage, BugMessage.visible == False).remove()
+            BugMessage, BugMessage.id.is_in(message_ids)).remove()
         self.store.flush()
         self.logger.info("Removed %d private bug messages.", count)
 
@@ -578,7 +587,11 @@
         # deletes because they fail (attempting to change a mutating table).
         # We can repair these caches by forcing the triggers to run for
         # every row.
-        self.store.execute("UPDATE BugMessage SET visible=visible")
+        self.store.execute("""
+            UPDATE Message SET visible=visible
+            FROM BugMessage
+            WHERE BugMessage.message = Message.id
+            """)
 
     def _fail(self, error_message):
         self.logger.fatal(error_message)

=== modified file 'lib/lp/services/configure.zcml'
--- lib/lp/services/configure.zcml	2011-03-07 16:32:12 +0000
+++ lib/lp/services/configure.zcml	2011-03-23 13:31:30 +0000
@@ -16,5 +16,6 @@
   <include package=".profile" />
   <include package=".salesforce" />
   <include package=".scripts" />
+  <include package=".session" />
   <include package=".worlddata" />
 </configure>

=== added directory 'lib/lp/services/session'
=== added file 'lib/lp/services/session/__init__.py'
=== added file 'lib/lp/services/session/adapters.py'
--- lib/lp/services/session/adapters.py	1970-01-01 00:00:00 +0000
+++ lib/lp/services/session/adapters.py	2011-03-23 13:31:30 +0000
@@ -0,0 +1,40 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Session adapters."""
+
+__metaclass__ = type
+__all__ = []
+
+
+from zope.component import adapter
+from zope.interface import implementer
+
+from canonical.database.sqlbase import session_store
+from canonical.launchpad.interfaces.lpstorm import (
+    IMasterStore,
+    ISlaveStore,
+    IStore,
+    )
+from lp.services.session.interfaces import IUseSessionStore
+
+
+@adapter(IUseSessionStore)
+@implementer(IMasterStore)
+def session_master_store(cls):
+    """Adapt a Session database object to an `IMasterStore`."""
+    return session_store()
+
+
+@adapter(IUseSessionStore)
+@implementer(ISlaveStore)
+def session_slave_store(cls):
+    """Adapt a Session database object to an `ISlaveStore`."""
+    return session_store()
+
+
+@adapter(IUseSessionStore)
+@implementer(IStore)
+def session_default_store(cls):
+    """Adapt an Session database object to an `IStore`."""
+    return session_store()

=== added file 'lib/lp/services/session/configure.zcml'
--- lib/lp/services/session/configure.zcml	1970-01-01 00:00:00 +0000
+++ lib/lp/services/session/configure.zcml	2011-03-23 13:31:30 +0000
@@ -0,0 +1,12 @@
+<!-- Copyright 2011 Canonical Ltd.  This software is licensed under the
+     GNU Affero General Public License version 3 (see the file LICENSE).
+-->
+<configure
+    xmlns="http://namespaces.zope.org/zope";
+    xmlns:browser="http://namespaces.zope.org/browser";
+    xmlns:i18n="http://namespaces.zope.org/i18n";
+    i18n_domain="launchpad">
+    <adapter factory=".adapters.session_master_store" />
+    <adapter factory=".adapters.session_slave_store" />
+    <adapter factory=".adapters.session_default_store" />
+</configure>

=== added file 'lib/lp/services/session/interfaces.py'
--- lib/lp/services/session/interfaces.py	1970-01-01 00:00:00 +0000
+++ lib/lp/services/session/interfaces.py	2011-03-23 13:31:30 +0000
@@ -0,0 +1,15 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Session interfaces."""
+
+__metaclass__ = type
+__all__ = ['IUseSessionStore']
+
+
+from zope.interface import Interface
+
+
+class IUseSessionStore(Interface):
+    """Marker interface for Session Storm database classes and instances."""
+    pass

=== added file 'lib/lp/services/session/model.py'
--- lib/lp/services/session/model.py	1970-01-01 00:00:00 +0000
+++ lib/lp/services/session/model.py	2011-03-23 13:31:30 +0000
@@ -0,0 +1,47 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Session Storm database classes"""
+
+__metaclass__ = type
+__all__ = ['SessionData', 'SessionPkgData']
+
+from storm.locals import (
+    Pickle,
+    Storm,
+    Unicode,
+    )
+from zope.interface import (
+    classProvides,
+    implements,
+    )
+
+from canonical.database.datetimecol import UtcDateTimeCol
+from lp.services.session.interfaces import IUseSessionStore
+
+
+class SessionData(Storm):
+    """A user's Session."""
+
+    classProvides(IUseSessionStore)
+    implements(IUseSessionStore)
+
+    __storm_table__ = 'SessionData'
+    client_id = Unicode(primary=True)
+    created = UtcDateTimeCol()
+    last_accessed = UtcDateTimeCol()
+
+
+class SessionPkgData(Storm):
+    """Data storage for a Session."""
+
+    classProvides(IUseSessionStore)
+    implements(IUseSessionStore)
+
+    __storm_table__ = 'SessionPkgData'
+    __storm_primary__ = 'client_id', 'product_id', 'key'
+
+    client_id = Unicode()
+    product_id = Unicode()
+    key = Unicode()
+    pickle = Pickle()

=== added directory 'lib/lp/services/session/tests'
=== added file 'lib/lp/services/session/tests/__init__.py'
=== added file 'lib/lp/services/session/tests/test_session.py'
--- lib/lp/services/session/tests/test_session.py	1970-01-01 00:00:00 +0000
+++ lib/lp/services/session/tests/test_session.py	2011-03-23 13:31:30 +0000
@@ -0,0 +1,32 @@
+# Copyright 2011 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Session tests."""
+
+__metaclass__ = type
+
+from canonical.launchpad.interfaces.lpstorm import (
+    IMasterStore,
+    ISlaveStore,
+    IStore,
+    )
+from canonical.testing.layers import DatabaseFunctionalLayer
+from lp.services.session.model import (
+    SessionData,
+    SessionPkgData,
+    )
+from lp.testing import TestCase
+
+
+class TestSessionModelAdapters(TestCase):
+    layer = DatabaseFunctionalLayer
+
+    def test_adapters(self):
+        for adapter in [IMasterStore, ISlaveStore, IStore]:
+            for cls in [SessionData, SessionPkgData]:
+                for obj in [cls, cls()]:
+                    store = adapter(obj)
+                    self.assert_(
+                        'session' in store.get_database()._dsn,
+                        'Unknown store returned adapting %r to %r'
+                        % (obj, adapter))

=== modified file 'lib/lp/soyuz/doc/archive-dependencies.txt'
--- lib/lp/soyuz/doc/archive-dependencies.txt	2010-10-18 12:35:41 +0000
+++ lib/lp/soyuz/doc/archive-dependencies.txt	2011-03-23 13:31:30 +0000
@@ -226,11 +226,11 @@
     0
 
     >>> print_building_sources_list(a_build)
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
         main restricted universe multiverse
 
 Once we publish a test binary in Celso's PPA hoary/i386,
@@ -243,11 +243,11 @@
 
     >>> print_building_sources_list(a_build)
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
         main restricted universe multiverse
 
 Similarly, unpopulated PPA dependencies are *not* listed in the building
@@ -259,11 +259,11 @@
     ...     getUtility(IComponentSet)['main'])
     >>> print_building_sources_list(a_build)
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
         main restricted universe multiverse
 
 But *populated* PPA dependencies *are* listed in the building 'sources_list'.
@@ -274,11 +274,11 @@
     >>> print_building_sources_list(a_build)
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
     deb http://ppa.launchpad.dev/mark/ppa/ubuntu hoary main
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
         main restricted universe multiverse
 
 The authentication information gets added for private PPA
@@ -300,11 +300,11 @@
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
     deb http://buildd:sekrit@xxxxxxxxxxxxxxxxxxxxxxxxx/mark/p3a/ubuntu
         hoary main
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
         main restricted universe multiverse
 
 Remove the private PPA dependency before continuing.
@@ -332,11 +332,11 @@
     ValueError: incomplete format
     <BLANKLINE>
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
         main restricted universe multiverse
 
 However, in order to avoid the problem going forward (and to allow the PPA
@@ -372,11 +372,11 @@
     ValueError: incomplete format
     <BLANKLINE>
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
         main restricted universe multiverse
 
 However, in order to avoid the problem going forward (and to allow the PPA
@@ -407,11 +407,11 @@
 
     >>> print_building_sources_list(a_build)
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
         main restricted universe multiverse
 
 The default build behaviour will remain unchanged when we override the
@@ -423,11 +423,11 @@
 
     >>> print_building_sources_list(a_build)
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
         main restricted universe multiverse
 
     >>> cprov.archive.removeArchiveDependency(ubuntu.main_archive)
@@ -450,9 +450,9 @@
 
     >>> print_building_sources_list(a_build)
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
-    deb http://ftpmaster.internal/ubuntu hoary
+    deb http://archive.launchpad.dev/ubuntu hoary
         main universe
-    deb http://ftpmaster.internal/ubuntu hoary-security
+    deb http://archive.launchpad.dev/ubuntu hoary-security
         main universe
 
     >>> cprov.archive.removeArchiveDependency(ubuntu.main_archive)
@@ -467,7 +467,7 @@
 
     >>> print_building_sources_list(a_build)
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
-    deb http://ftpmaster.internal/ubuntu hoary main restricted
+    deb http://archive.launchpad.dev/ubuntu hoary main restricted
 
     >>> cprov.archive.removeArchiveDependency(ubuntu.main_archive)
 
@@ -480,13 +480,13 @@
 
     >>> print_building_sources_list(a_build)
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-proposed
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-proposed
         main restricted universe multiverse
 
     >>> cprov.archive.removeArchiveDependency(ubuntu.main_archive)
@@ -499,13 +499,13 @@
 
     >>> print_building_sources_list(a_build)
     deb http://ppa.launchpad.dev/cprov/ppa/ubuntu hoary main
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-backports
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-backports
         main restricted universe multiverse
 
     >>> cprov.archive.removeArchiveDependency(ubuntu.main_archive)
@@ -532,12 +532,12 @@
     >>> [partner_build] = pub_source.createMissingBuilds()
 
     >>> print_building_sources_list(partner_build)
-    deb http://ftpmaster.internal/ubuntu-partner hoary partner
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
+    deb http://archive.launchpad.dev/ubuntu-partner hoary partner
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
         main restricted universe multiverse
 
 
@@ -570,9 +570,9 @@
     deb http://user:pass@repository zoing everything
     deb http://user:pass@repository hoary public private
     deb http://user:pass@repository hoary-extra public
-    deb http://ftpmaster.internal/ubuntu hoary
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-security
-        main restricted universe multiverse
-    deb http://ftpmaster.internal/ubuntu hoary-updates
+    deb http://archive.launchpad.dev/ubuntu hoary
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-security
+        main restricted universe multiverse
+    deb http://archive.launchpad.dev/ubuntu hoary-updates
         main restricted universe multiverse

=== modified file 'lib/lp/soyuz/doc/archive.txt'
--- lib/lp/soyuz/doc/archive.txt	2011-03-03 00:43:44 +0000
+++ lib/lp/soyuz/doc/archive.txt	2011-03-23 13:31:30 +0000
@@ -1284,21 +1284,23 @@
     None
 
 IArchive.archive_url will return a URL for the archive that the builder can
-use to retrieve files from it.
+use to retrieve files from it.  Internal paths and urls supplied via the
+PunlisherConfig require us to log in as an admin:
 
+    >>> login('admin@xxxxxxxxxxxxx')
     >>> print partner_archive.archive_url
-    http://ftpmaster.internal/ubuntutest-partner
+    http://archive.launchpad.dev/ubuntutest-partner
 
     >>> print sandbox_archive.archive_url
     http://ppa.launchpad.dev/name16/ppa/ubuntu
 
     >>> print getUtility(IArchiveSet).getByDistroPurpose(
     ...     ubuntutest, ArchivePurpose.PRIMARY).archive_url
-    http://ftpmaster.internal/ubuntutest
+    http://archive.launchpad.dev/ubuntutest
 
     >>> print getUtility(IArchiveSet).getByDistroPurpose(
     ...     ubuntu, ArchivePurpose.DEBUG).archive_url
-    http://ftpmaster.internal/ubuntu-debug
+    http://archive.launchpad.dev/ubuntu-debug
 
 COPY archives use a URL format of <distro-name>-<archive-name>:
 
@@ -1310,6 +1312,7 @@
 If the archive is private, the url may be different as private PPAs
 are published to a secure location.
 
+    >>> login("celso.providelo@xxxxxxxxxxxxx")
     >>> print cprov_archive.archive_url
     http://ppa.launchpad.dev/cprov/ppa/ubuntu
 
@@ -2460,7 +2463,7 @@
     >>> uber = getUtility(IDistributionSet).new(
     ... 'uberdistro', 'The uberdistro', 'The mother of all distros',
     ... 'All you would want from a distro', 'zero', 'uberdistro.org',
-    ... mark, cprov)
+    ... mark, cprov, cprov)
 
 The primary archive for the Ãœberdistro was created by the
 IDistributionSet.new() method. Let's check its publish flag.

=== modified file 'lib/lp/soyuz/doc/distroseriesqueue-translations.txt'
--- lib/lp/soyuz/doc/distroseriesqueue-translations.txt	2011-03-22 14:27:50 +0000
+++ lib/lp/soyuz/doc/distroseriesqueue-translations.txt	2011-03-23 13:31:30 +0000
@@ -158,16 +158,16 @@
   ...     print '%s/%s by %s: %s' % (
   ...         entry.distroseries.name, entry.sourcepackagename.name,
   ...         entry.importer.name, entry.path)
-  dapper/pmount by mark: po/es_ES.po
-  dapper/pmount by mark: po/ca.po
-  dapper/pmount by mark: po/de.po
-  dapper/pmount by mark: po/cs.po
-  dapper/pmount by mark: po/es.po
-  dapper/pmount by mark: po/fr.po
-  dapper/pmount by mark: po/hr.po
-  dapper/pmount by mark: po/nb.po
-  dapper/pmount by mark: po/pmount.pot
-  dapper/pmount by mark: po/it_IT.po
+  dapper/pmount by ubuntu-team: po/es_ES.po
+  dapper/pmount by ubuntu-team: po/ca.po
+  dapper/pmount by ubuntu-team: po/de.po
+  dapper/pmount by ubuntu-team: po/cs.po
+  dapper/pmount by ubuntu-team: po/es.po
+  dapper/pmount by ubuntu-team: po/fr.po
+  dapper/pmount by ubuntu-team: po/hr.po
+  dapper/pmount by ubuntu-team: po/nb.po
+  dapper/pmount by ubuntu-team: po/pmount.pot
+  dapper/pmount by ubuntu-team: po/it_IT.po
 
   # Abort the transaction so we can check the same upload in a different
   # pocket.
@@ -208,16 +208,16 @@
   ...     print '%s/%s by %s: %s' % (
   ...         entry.distroseries.name, entry.sourcepackagename.name,
   ...         entry.importer.name, entry.path)
-  dapper/pmount by mark: po/es_ES.po
-  dapper/pmount by mark: po/ca.po
-  dapper/pmount by mark: po/de.po
-  dapper/pmount by mark: po/cs.po
-  dapper/pmount by mark: po/es.po
-  dapper/pmount by mark: po/fr.po
-  dapper/pmount by mark: po/hr.po
-  dapper/pmount by mark: po/nb.po
-  dapper/pmount by mark: po/pmount.pot
-  dapper/pmount by mark: po/it_IT.po
+  dapper/pmount by ubuntu-team: po/es_ES.po
+  dapper/pmount by ubuntu-team: po/ca.po
+  dapper/pmount by ubuntu-team: po/de.po
+  dapper/pmount by ubuntu-team: po/cs.po
+  dapper/pmount by ubuntu-team: po/es.po
+  dapper/pmount by ubuntu-team: po/fr.po
+  dapper/pmount by ubuntu-team: po/hr.po
+  dapper/pmount by ubuntu-team: po/nb.po
+  dapper/pmount by ubuntu-team: po/pmount.pot
+  dapper/pmount by ubuntu-team: po/it_IT.po
 
   # Let's abort the transaction so we can check the same upload in a different
   # pocket.
@@ -241,16 +241,16 @@
   ...     print '%s/%s by %s: %s' % (
   ...         entry.distroseries.name, entry.sourcepackagename.name,
   ...         entry.importer.name, entry.path)
-  dapper/pmount by mark: po/es_ES.po
-  dapper/pmount by mark: po/ca.po
-  dapper/pmount by mark: po/de.po
-  dapper/pmount by mark: po/cs.po
-  dapper/pmount by mark: po/es.po
-  dapper/pmount by mark: po/fr.po
-  dapper/pmount by mark: po/hr.po
-  dapper/pmount by mark: po/nb.po
-  dapper/pmount by mark: po/pmount.pot
-  dapper/pmount by mark: po/it_IT.po
+  dapper/pmount by ubuntu-team: po/es_ES.po
+  dapper/pmount by ubuntu-team: po/ca.po
+  dapper/pmount by ubuntu-team: po/de.po
+  dapper/pmount by ubuntu-team: po/cs.po
+  dapper/pmount by ubuntu-team: po/es.po
+  dapper/pmount by ubuntu-team: po/fr.po
+  dapper/pmount by ubuntu-team: po/hr.po
+  dapper/pmount by ubuntu-team: po/nb.po
+  dapper/pmount by ubuntu-team: po/pmount.pot
+  dapper/pmount by ubuntu-team: po/it_IT.po
 
   # Let's abort the transaction so we can check the same upload in a different
   # component.

=== modified file 'lib/lp/soyuz/doc/publishing.txt'
--- lib/lp/soyuz/doc/publishing.txt	2011-03-21 10:47:58 +0000
+++ lib/lp/soyuz/doc/publishing.txt	2011-03-23 13:31:30 +0000
@@ -362,7 +362,7 @@
     >>> [(pub_file.libraryfilealias.filename, pub_file.file_type_name,
     ...   pub_file.archive_url) for pub_file in spph.files]
     [(u'alsa-utils_1.0.8-1ubuntu1.dsc', 'dsc',
-      u'http://ftpmaster.internal/ubuntu/pool/main/a/alsa-utils/alsa-utils_1.0.8-1ubuntu1.dsc')]
+      u'http://archive.launchpad.dev/ubuntu/pool/main/a/alsa-utils/alsa-utils_1.0.8-1ubuntu1.dsc')]
 
 
 Deletion and obsolescence
@@ -952,8 +952,11 @@
     >>> [pub_file.libraryfilealias.filename for pub_file in bpph.files]
     [u'mozilla-firefox_0.9_i386.deb']
 
+    >>> debian = bpph.distroarchseries.distroseries.distribution
+    >>> discard = factory.makePublisherConfig(
+    ...     distribution=debian, base_url=u"http://archive.launchpad.dev";)
     >>> [pub_file.archive_url for pub_file in bpph.files]
-    [u'http://ftpmaster.internal/debian/pool/universe/m/mozilla-firefox/mozilla-firefox_0.9_i386.deb']
+    [u'http://archive.launchpad.dev/debian/pool/universe/m/mozilla-firefox/mozilla-firefox_0.9_i386.deb']
 
 Binary publishing records also have a download count, which contains
 the number of downloads of this binary package release in this archive.

=== modified file 'lib/lp/soyuz/interfaces/distroarchseries.py'
--- lib/lp/soyuz/interfaces/distroarchseries.py	2010-10-06 11:46:51 +0000
+++ lib/lp/soyuz/interfaces/distroarchseries.py	2011-03-23 13:31:30 +0000
@@ -30,6 +30,7 @@
     )
 
 from canonical.launchpad import _
+from lp.app.validators.name import name_validator
 from lp.registry.interfaces.distroseries import IDistroSeries
 from lp.registry.interfaces.person import IPerson
 from lp.registry.interfaces.role import IHasOwner
@@ -56,7 +57,8 @@
                 "identifies this architecture. All binary packages in the "
                 "archive will use this tag in their filename. Please get it "
                 "correct. It should really never be changed!"),
-            required=True),
+            required=True,
+            constraint=name_validator),
         exported_as="architecture_tag")
     official = exported(
         Bool(

=== modified file 'lib/lp/soyuz/model/archive.py'
--- lib/lp/soyuz/model/archive.py	2011-03-22 04:40:12 +0000
+++ lib/lp/soyuz/model/archive.py	2011-03-23 13:31:30 +0000
@@ -39,7 +39,6 @@
     alsoProvides,
     implements,
     )
-from zope.security.proxy import removeSecurityProxy
 
 from canonical.config import config
 from canonical.database.constants import UTC_NOW
@@ -76,6 +75,7 @@
 from lp.app.errors import NotFoundError
 from lp.app.validators.name import valid_name
 from lp.archivepublisher.debversion import Version
+from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
 from lp.archiveuploader.utils import (
     re_isadeb,
     re_issource,
@@ -423,9 +423,11 @@
                 url, "/".join(
                     (self.owner.name, self.name, self.distribution.name)))
 
+        db_pubconf = getUtility(
+            IPublisherConfigSet).getByDistribution(self.distribution)
         if self.is_copy:
             url = urlappend(
-                config.archivepublisher.copy_base_url,
+                db_pubconf.copy_base_url,
                 self.distribution.name + '-' + self.name)
             return urlappend(url, self.distribution.name)
 
@@ -435,8 +437,7 @@
             raise AssertionError(
                 "archive_url unknown for purpose: %s" % self.purpose)
         return urlappend(
-            config.archivepublisher.base_url,
-            self.distribution.name + postfix)
+            db_pubconf.base_url, self.distribution.name + postfix)
 
     @property
     def signing_key_fingerprint(self):

=== modified file 'lib/lp/soyuz/scripts/tests/test_publishdistro.py'
--- lib/lp/soyuz/scripts/tests/test_publishdistro.py	2010-12-20 03:21:03 +0000
+++ lib/lp/soyuz/scripts/tests/test_publishdistro.py	2011-03-23 13:31:30 +0000
@@ -17,6 +17,7 @@
 
 from canonical.config import config
 from lp.archivepublisher.config import getPubConfig
+from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.person import IPersonSet
 from lp.services.log.logger import BufferLogger
@@ -274,13 +275,15 @@
             self.runPublishDistro, ['--primary-debug'])
 
         # The DEBUG repository path was not created.
+        ubuntutest = getUtility(IDistributionSet)['ubuntutest']
+        root_dir = getUtility(
+            IPublisherConfigSet).getByDistribution(ubuntutest).root_dir
         repo_path = os.path.join(
-            config.archivepublisher.root, 'ubuntutest-debug')
+            root_dir, 'ubuntutest-debug')
         self.assertNotExists(repo_path)
 
         # We will create the DEBUG archive for ubuntutest, so it can
         # be published.
-        ubuntutest = getUtility(IDistributionSet)['ubuntutest']
         debug_archive = getUtility(IArchiveSet).new(
             purpose=ArchivePurpose.DEBUG, owner=ubuntutest.owner,
             distribution=ubuntutest)
@@ -319,8 +322,10 @@
         copy_archive_name = 'test-copy-publish'
 
         # The COPY repository path is not created yet.
+        root_dir = getUtility(
+            IPublisherConfigSet).getByDistribution(ubuntutest).root_dir
         repo_path = os.path.join(
-            config.archivepublisher.root,
+            root_dir,
             ubuntutest.name + '-' + copy_archive_name,
             ubuntutest.name)
         self.assertNotExists(repo_path)

=== modified file 'lib/lp/soyuz/stories/distroseries/add-architecture.txt'
--- lib/lp/soyuz/stories/distroseries/add-architecture.txt	2009-11-09 17:08:21 +0000
+++ lib/lp/soyuz/stories/distroseries/add-architecture.txt	2011-03-23 13:31:30 +0000
@@ -27,6 +27,16 @@
     >>> print admin_browser.title
     ia64 : Hoary (5.04) : Ubuntu
 
+Architecture tag is restricted to the usual Launchpad name format.
+
+    >>> admin_browser.open('http://launchpad.dev/ubuntu/hoary')
+    >>> admin_browser.getLink('Add architecture').click()
+    >>> admin_browser.getControl('Architecture Tag').value = 'foo bar'
+    >>> admin_browser.getControl('Continue').click()
+    >>> print "\n".join(get_feedback_messages(admin_browser.contents))
+    There is 1 error.
+    Invalid name 'foo bar'. ...
+
 Other users won't see the link nor the page where a new port can be
 registered.
 

=== modified file 'lib/lp/soyuz/tests/test_packageupload.py'
--- lib/lp/soyuz/tests/test_packageupload.py	2010-12-20 06:46:09 +0000
+++ lib/lp/soyuz/tests/test_packageupload.py	2011-03-23 13:31:30 +0000
@@ -12,6 +12,7 @@
 from canonical.config import config
 from canonical.testing.layers import LaunchpadZopelessLayer
 from lp.archiveuploader.tests import datadir
+from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
 from lp.buildmaster.enums import BuildStatus
 from lp.registry.interfaces.distribution import IDistributionSet
 from lp.registry.interfaces.pocket import PackagePublishingPocket
@@ -158,22 +159,26 @@
             self.assertEqual(
                 restricted, pub_file.libraryfilealias.restricted)
 
-    def removeRepository(self):
+    def removeRepository(self, distro):
         """Remove the testing repository root if it exists."""
-        if os.path.exists(config.archivepublisher.root):
-            shutil.rmtree(config.archivepublisher.root)
+        root = getUtility(
+            IPublisherConfigSet).getByDistribution(distro).root_dir
+        if os.path.exists(root):
+            shutil.rmtree(root)
 
     def test_realiseUpload_for_delayed_copies(self):
         # Delayed-copies result in published records that were overridden
         # and has their files privacy adjusted according test destination
         # context.
 
+        # Create the default delayed-copy context.
+        delayed_copy = self.createDelayedCopy()
+
         # Add a cleanup for removing the repository where the custom upload
         # was published.
-        self.addCleanup(self.removeRepository)
-
-        # Create the default delayed-copy context.
-        delayed_copy = self.createDelayedCopy()
+        self.addCleanup(
+            self.removeRepository,
+            self.test_publisher.breezy_autotest.distribution)
 
         # Delayed-copies targeted to unreleased pockets cannot be accepted.
         self.assertRaisesWithContent(
@@ -216,6 +221,7 @@
         # production.  The user's environment might have a different umask, so
         # just force it to what the test expects.
         old_umask = os.umask(022)
+
         try:
             pub_records = delayed_copy.realiseUpload(logger=logger)
         finally:
@@ -271,8 +277,10 @@
         self.assertFalse(package_diff.diff_content.restricted)
 
         # The custom file was also published.
+        root_dir = getUtility(IPublisherConfigSet).getByDistribution(
+            self.test_publisher.breezy_autotest.distribution).root_dir
         custom_path = os.path.join(
-            config.archivepublisher.root,
+            root_dir,
             'ubuntutest/dists/breezy-autotest-security',
             'main/dist-upgrader-all')
         self.assertEquals(

=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py	2011-03-18 10:31:56 +0000
+++ lib/lp/testing/factory.py	2011-03-23 13:31:30 +0000
@@ -101,6 +101,7 @@
     )
 from canonical.launchpad.webapp.sorting import sorted_version_numbers
 from lp.app.enums import ServiceUsage
+from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
 from lp.archiveuploader.dscfile import DSCFile
 from lp.archiveuploader.uploadpolicy import BuildDaemonUploadPolicy
 from lp.blueprints.enums import (
@@ -2216,8 +2217,10 @@
         return library_file_alias
 
     def makeDistribution(self, name=None, displayname=None, owner=None,
-                         members=None, title=None, aliases=None,
-                         bug_supervisor=None):
+                         registrant=None, members=None, title=None,
+                         aliases=None, bug_supervisor=None,
+                         publish_root_dir=None, publish_base_url=None,
+                         publish_copy_base_url=None, no_pubconf=False):
         """Make a new distribution."""
         if name is None:
             name = self.getUniqueString(prefix="distribution")
@@ -2228,23 +2231,30 @@
         description = self.getUniqueString()
         summary = self.getUniqueString()
         domainname = self.getUniqueString()
+        if registrant is None:
+            registrant = self.makePerson()
         if owner is None:
             owner = self.makePerson()
         if members is None:
             members = self.makeTeam(owner)
         distro = getUtility(IDistributionSet).new(
             name, displayname, title, description, summary, domainname,
-            members, owner)
+            members, owner, registrant)
         if aliases is not None:
             removeSecurityProxy(distro).setAliases(aliases)
         if bug_supervisor is not None:
             naked_distro = removeSecurityProxy(distro)
             naked_distro.bug_supervisor = bug_supervisor
+        if not no_pubconf:
+            self.makePublisherConfig(
+                distro, publish_root_dir, publish_base_url,
+                publish_copy_base_url)
         return distro
 
     def makeDistroRelease(self, distribution=None, version=None,
                           status=SeriesStatus.DEVELOPMENT,
-                          parent_series=None, name=None, displayname=None):
+                          parent_series=None, name=None, displayname=None,
+                          registrant=None):
         """Make a new distro release."""
         if distribution is None:
             distribution = self.makeDistribution()
@@ -2254,6 +2264,8 @@
             displayname = name.capitalize()
         if version is None:
             version = "%s.0" % self.getUniqueInteger()
+        if registrant is None:
+            registrant = distribution.owner
 
         # We don't want to login() as the person used to create the product,
         # so we remove the security proxy before creating the series.
@@ -2264,7 +2276,7 @@
             displayname=displayname,
             title=self.getUniqueString(), summary=self.getUniqueString(),
             description=self.getUniqueString(),
-            parent_series=parent_series, owner=distribution.owner)
+            parent_series=parent_series, registrant=registrant)
         series.status = status
         return ProxyFactory(series)
 
@@ -3899,6 +3911,20 @@
             description = self.getUniqueString()
         return getUtility(ICveSet).new(sequence, description, cvestate)
 
+    def makePublisherConfig(self, distribution=None, root_dir=None,
+                            base_url=None, copy_base_url=None):
+        """Create a new `PublisherConfig` record."""
+        if distribution is None:
+            distribution = self.makeDistribution()
+        if root_dir is None:
+            root_dir = self.getUniqueUnicode()
+        if base_url is None:
+            base_url = self.getUniqueUnicode()
+        if copy_base_url is None:
+            copy_base_url = self.getUniqueUnicode()
+        return getUtility(IPublisherConfigSet).new(
+            distribution, root_dir, base_url, copy_base_url)
+
 
 # Some factory methods return simple Python types. We don't add
 # security wrappers for them, as well as for objects created by

=== modified file 'lib/lp/testing/tests/test_standard_test_template.py'
--- lib/lp/testing/tests/test_standard_test_template.py	2011-02-02 13:19:02 +0000
+++ lib/lp/testing/tests/test_standard_test_template.py	2011-03-23 13:31:30 +0000
@@ -6,15 +6,16 @@
 __metaclass__ = type
 
 from canonical.testing.layers import DatabaseFunctionalLayer
-from lp.testing import TestCase # or TestCaseWithFactory
+# or TestCaseWithFactory
+from lp.testing import TestCase
 
 
 class TestSomething(TestCase):
     # XXX: Sample test class.  Replace with your own test class(es).
 
-    # XXX: Optional layer--see lib/canonical/testing/layers.py
-    # Get the simplest layer that your test will work on, or if you
-    # don't even use the database, don't set it at all.
+    # XXX: layer--see lib/canonical/testing/layers.py
+    # Get the simplest layer that your test will work on. For unit tests
+    # requiring no resources, this is BaseLayer.
     layer = DatabaseFunctionalLayer
 
     # XXX: Sample test.  Replace with your own test methods.

=== modified file 'lib/lp/translations/browser/tests/test_sharing_information.py'
--- lib/lp/translations/browser/tests/test_sharing_information.py	2011-03-08 11:01:41 +0000
+++ lib/lp/translations/browser/tests/test_sharing_information.py	2011-03-23 13:31:30 +0000
@@ -1,15 +1,15 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Tests for the POTemplate recipe view classes and templates."""
 
 __metaclass__ = type
 
-from canonical.testing.layers import DatabaseFunctionalLayer
 from canonical.launchpad.testing.pages import (
     extract_text,
     find_tag_by_id,
     )
+from canonical.testing.layers import DatabaseFunctionalLayer
 from lp.app.enums import ServiceUsage
 from lp.services.features.testing import FeatureFixture
 from lp.testing import (
@@ -253,7 +253,8 @@
 
     def getAuthorizedUser(self, potemplate):
         with celebrity_logged_in('admin'):
-            potemplate.distroseries.owner = self.factory.makePerson(
+            distribution = potemplate.distroseries.distribution
+            distribution.owner = self.factory.makePerson(
                 password='test')
         return potemplate.distroseries.owner
 
@@ -283,6 +284,7 @@
 
     def getAuthorizedUser(self, sourcepackage):
         with celebrity_logged_in('admin'):
-            sourcepackage.distroseries.owner = self.factory.makePerson(
+            makePerson = self.factory.makePerson
+            sourcepackage.distroseries.distribution.owner = makePerson(
                 password='test')
         return sourcepackage.distroseries.owner

=== modified file 'utilities/soyuz-sampledata-setup.py'
--- utilities/soyuz-sampledata-setup.py	2011-02-27 08:21:54 +0000
+++ utilities/soyuz-sampledata-setup.py	2011-03-23 13:31:30 +0000
@@ -218,14 +218,14 @@
 def create_series(parent, full_name, version, status):
     """Set up a `DistroSeries`."""
     distribution = parent.distribution
-    owner = parent.owner
+    registrant = parent.owner
     name = full_name.split()[0].lower()
     title = "The " + full_name
     displayname = full_name.split()[0]
     new_series = distribution.newSeries(name=name, title=title,
         displayname=displayname, summary='Ubuntu %s is good.' % version,
         description='%s is awesome.' % version, version=version,
-        parent_series=parent, owner=owner)
+        parent_series=parent, registrant=registrant)
     new_series.status = status
     notify(ObjectCreatedEvent(new_series))
 


Follow ups