← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~bac/launchpad/bug-753965 into lp:launchpad

 

Brad Crittenden has proposed merging lp:~bac/launchpad/bug-753965 into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #753965 in Launchpad itself: "An IStructuralSubscriptionTarget that does not use LP for bug tracking should not present a subscription link"
  https://bugs.launchpad.net/launchpad/+bug/753965

For more details, see:
https://code.launchpad.net/~bac/launchpad/bug-753965/+merge/57262

= Summary =

If the IStructuralSubscriptionTarget does not use LP for bug tracking
then the structural subscription links should not be shown.

== Proposed fix ==

Check the service usage before enabling the link.  Also don't call
structural-subscription.setup from the page template unless Launchpad is
used.

== Pre-implementation notes ==

None.

== Implementation details ==

As above.

== Tests ==

bin/test -vvt test_subscription_links

== Demo and Q/A ==

Create a new project in launchpad.dev.  Note that the subscription links
are present.  Go to 'Configure bug tracker' and mark it as external.
Note the links no longer appear.

= Launchpad lint =

Lint will be fixed.

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/templates/product-index.pt
  lib/lp/registry/templates/milestone-index.pt
  lib/lp/registry/templates/distroseries-index.pt
  lib/lp/registry/templates/distribution-index.pt
  lib/lp/registry/templates/distributionsourcepackage-index.pt
  lib/lp/bugs/browser/structuralsubscription.py
  lib/lp/bugs/model/structuralsubscription.py
  lib/lp/registry/templates/project-index.pt
  lib/lp/registry/templates/productseries-index.pt
  lib/lp/registry/browser/tests/test_subscription_links.py

./lib/lp/bugs/browser/structuralsubscription.py
     443: Line exceeds 78 characters.
./lib/lp/registry/browser/tests/test_subscription_links.py
     641: E302 expected 2 blank lines, found 1
     642: E301 expected 1 blank line, found 0
     649: E301 expected 1 blank line, found 0
     681: E301 expected 1 blank line, found 0
     747: E301 expected 1 blank line, found 0
     801: E301 expected 1 blank line, found 0
-- 
https://code.launchpad.net/~bac/launchpad/bug-753965/+merge/57262
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~bac/launchpad/bug-753965 into lp:launchpad.
=== modified file 'lib/lp/bugs/browser/structuralsubscription.py'
--- lib/lp/bugs/browser/structuralsubscription.py	2011-04-06 15:58:38 +0000
+++ lib/lp/bugs/browser/structuralsubscription.py	2011-04-12 00:26:00 +0000
@@ -49,6 +49,7 @@
     custom_widget,
     LaunchpadFormView,
     )
+from lp.app.enums import ServiceUsage
 from lp.app.widgets.itemswidgets import LabeledMultiCheckBoxWidget
 from lp.bugs.interfaces.bugtask import (
     BugTaskImportance,
@@ -364,25 +365,34 @@
         else:
             text = 'Subscribe to bug mail'
             icon = 'add'
-        if not enabled or (
-            not sst.userCanAlterBugSubscription(self.user, self.user)):
+        if not sst.userCanAlterBugSubscription(self.user, self.user):
             enabled = False
         return Link('+subscribe', text, icon=icon, enabled=enabled)
 
+    @property
+    def _enabled(self):
+        """Should the link be enabled?
+
+        True if the target uses Launchpad for bugs and the user can alter the
+        bug subscriptions.
+        """
+        sst = self._getSST()
+        target = sst
+        if sst.parent_subscription_target is not None:
+            target = sst.parent_subscription_target
+        return (target.bug_tracking_usage == ServiceUsage.LAUNCHPAD and
+                sst.userCanAlterBugSubscription(self.user, self.user))
+
     @enabled_with_permission('launchpad.AnyPerson')
     def subscribe_to_bug_mail(self):
-        sst = self._getSST()
-        enabled = sst.userCanAlterBugSubscription(self.user, self.user)
         text = 'Subscribe to bug mail'
-        return Link('#', text, icon='add', hidden=True, enabled=enabled)
+        return Link('#', text, icon='add', hidden=True, enabled=self._enabled)
 
     @enabled_with_permission('launchpad.AnyPerson')
     def edit_bug_mail(self):
-        sst = self._getSST()
-        enabled = sst.userCanAlterBugSubscription(self.user, self.user)
         text = 'Edit bug mail'
         return Link('+subscriptions', text, icon='edit', site='bugs',
-                    enabled=enabled)
+                    enabled=self._enabled)
 
 
 def expose_structural_subscription_data_to_js(context, request,

=== modified file 'lib/lp/bugs/model/structuralsubscription.py'
--- lib/lp/bugs/model/structuralsubscription.py	2011-03-29 05:38:15 +0000
+++ lib/lp/bugs/model/structuralsubscription.py	2011-04-12 00:26:00 +0000
@@ -355,6 +355,7 @@
 
         # Nobody else can, unless the context is a IDistributionSourcePackage,
         # in which case the drivers or owner can.
+
         if IDistributionSourcePackage.providedBy(self):
             for driver in self.distribution.drivers:
                 if subscribed_by.inTeam(driver):

=== modified file 'lib/lp/registry/browser/tests/test_subscription_links.py'
--- lib/lp/registry/browser/tests/test_subscription_links.py	2011-04-04 20:15:16 +0000
+++ lib/lp/registry/browser/tests/test_subscription_links.py	2011-04-12 00:26:00 +0000
@@ -52,7 +52,7 @@
             self.contents, 'menu-link-edit_bug_mail')
 
     def assertOldLinkMissing(self):
-        self.assertEqual(None, self.old_link)
+        self.assertEqual(None, self.old_link, self.contents)
 
     def assertOldLinkPresent(self):
         self.assertNotEqual(None, self.old_link)
@@ -110,7 +110,7 @@
 
     def test_subscribe_link_feature_flag_off_user(self):
         self._create_scenario(self.regular_user, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_user(self):
@@ -122,7 +122,7 @@
         self._create_scenario(ANONYMOUS, None)
         # The old subscribe link is actually shown to anonymous users but the
         # behavior has changed with the new link.
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_anonymous(self):
@@ -132,75 +132,76 @@
         self.assertNewLinksMissing()
 
 
-class TestProductViewStructSubs(_TestStructSubs):
+class ProductView(_TestStructSubs):
     """Test structural subscriptions on the product view."""
 
     rootsite = None
     view = '+index'
 
     def setUp(self):
-        super(TestProductViewStructSubs, self).setUp()
+        super(ProductView, self).setUp()
         self.target = self.factory.makeProduct(official_malone=True)
 
 
-class TestProductBugsStructSubs(TestProductViewStructSubs):
+class ProductBugs(ProductView):
     """Test structural subscriptions on the product bugs view."""
 
     rootsite = 'bugs'
     view = '+bugs-index'
 
 
-class TestProjectGroupViewStructSubs(_TestStructSubs):
+class ProjectGroupView(_TestStructSubs):
     """Test structural subscriptions on the project group view."""
 
     rootsite = None
     view = '+index'
 
     def setUp(self):
-        super(TestProjectGroupViewStructSubs, self).setUp()
+        super(ProjectGroupView, self).setUp()
         self.target = self.factory.makeProject()
         self.factory.makeProduct(
             project=self.target, official_malone=True)
 
 
-class TestProjectGroupBugsStructSubs(TestProjectGroupViewStructSubs):
+class ProjectGroupBugs(ProjectGroupView):
     """Test structural subscriptions on the project group bugs view."""
 
     rootsite = 'bugs'
     view = '+bugs'
 
 
-class TestProductSeriesViewStructSubs(_TestStructSubs):
+class ProductSeriesView(_TestStructSubs):
     """Test structural subscriptions on the product series view."""
 
     rootsite = None
     view = '+index'
 
     def setUp(self):
-        super(TestProductSeriesViewStructSubs, self).setUp()
-        self.target = self.factory.makeProductSeries()
-
-
-class TestProductSeriesBugsStructSubs(TestProductSeriesViewStructSubs):
+        super(ProductSeriesView, self).setUp()
+        product = self.factory.makeProduct(official_malone=True)
+        self.target = self.factory.makeProductSeries(product=product)
+
+
+class ProductSeriesBugs(ProductSeriesView):
     """Test structural subscriptions on the product series bugs view."""
 
     rootsite = 'bugs'
     view = '+bugs-index'
 
     def setUp(self):
-        super(TestProductSeriesBugsStructSubs, self).setUp()
+        super(ProductSeriesBugs, self).setUp()
         with person_logged_in(self.target.product.owner):
             self.target.product.official_malone = True
 
 
-class TestDistributionSourcePackageViewStructSubs(_TestStructSubs):
+class DistributionSourcePackageView(_TestStructSubs):
     """Test structural subscriptions on the distro src pkg view."""
 
     rootsite = None
     view = '+index'
 
     def setUp(self):
-        super(TestDistributionSourcePackageViewStructSubs, self).setUp()
+        super(DistributionSourcePackageView, self).setUp()
         distro = self.factory.makeDistribution()
         with person_logged_in(distro.owner):
             distro.official_malone = True
@@ -213,15 +214,14 @@
     test_subscribe_link_feature_flag_on_owner = None
 
 
-class TestDistributionSourcePackageBugsStructSubs(
-    TestDistributionSourcePackageViewStructSubs):
+class DistributionSourcePackageBugs(DistributionSourcePackageView):
     """Test structural subscriptions on the distro src pkg bugs view."""
 
     rootsite = 'bugs'
     view = '+bugs'
 
 
-class TestDistroViewStructSubs(BrowserTestCase, _TestResultsMixin):
+class DistroView(BrowserTestCase, _TestResultsMixin):
     """Test structural subscriptions on the distribution view.
 
     Distributions are special.  They are IStructuralSubscriptionTargets but
@@ -238,7 +238,7 @@
     view = '+index'
 
     def setUp(self):
-        super(TestDistroViewStructSubs, self).setUp()
+        super(DistroView, self).setUp()
         self.target = self.factory.makeDistribution()
         with person_logged_in(self.target.owner):
             self.target.official_malone = True
@@ -329,14 +329,13 @@
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_admin(self):
-        from lp.testing.sampledata import ADMIN_EMAIL
         admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
         self._create_scenario(admin, 'on')
         self.assertOldLinkMissing()
         self.assertNewLinksPresent()
 
 
-class TestDistroBugsStructSubs(TestDistroViewStructSubs):
+class DistroBugs(DistroView):
     """Test structural subscriptions on the distro bugs view."""
 
     rootsite = 'bugs'
@@ -344,7 +343,7 @@
 
     def test_subscribe_link_feature_flag_off_owner(self):
         self._create_scenario(self.target.owner, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_owner(self):
@@ -354,7 +353,7 @@
 
     def test_subscribe_link_feature_flag_off_user(self):
         self._create_scenario(self.regular_user, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_user_no_bug_super(self):
@@ -374,7 +373,7 @@
 
     def test_subscribe_link_feature_flag_off_anonymous(self):
         self._create_scenario(ANONYMOUS, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_anonymous(self):
@@ -388,7 +387,7 @@
             self.target.setBugSupervisor(
                 self.regular_user, admin)
         self._create_scenario(self.regular_user, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_bug_super(self):
@@ -403,7 +402,7 @@
     def test_subscribe_link_feature_flag_off_admin(self):
         admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
         self._create_scenario(admin, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_admin(self):
@@ -414,17 +413,17 @@
         self.assertNewLinksPresent()
 
 
-class TestDistroMilestoneViewStructSubs(TestDistroViewStructSubs):
+class DistroMilestoneView(DistroView):
     """Test structural subscriptions on the distro milestones."""
 
     def setUp(self):
-        super(TestDistroMilestoneViewStructSubs, self).setUp()
+        super(DistroMilestoneView, self).setUp()
         self.distro = self.target
         self.target = self.factory.makeMilestone(distribution=self.distro)
 
     def test_subscribe_link_feature_flag_off_owner(self):
         self._create_scenario(self.distro.owner, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_owner(self):
@@ -434,7 +433,7 @@
 
     def test_subscribe_link_feature_flag_off_user(self):
         self._create_scenario(self.regular_user, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_user_no_bug_super(self):
@@ -454,7 +453,7 @@
 
     def test_subscribe_link_feature_flag_off_anonymous(self):
         self._create_scenario(ANONYMOUS, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_anonymous(self):
@@ -468,7 +467,7 @@
             self.distro.setBugSupervisor(
                 self.regular_user, admin)
         self._create_scenario(self.regular_user, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_bug_super(self):
@@ -483,7 +482,7 @@
     def test_subscribe_link_feature_flag_off_admin(self):
         admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
         self._create_scenario(admin, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_admin(self):
@@ -494,11 +493,11 @@
         self.assertNewLinksPresent()
 
 
-class TestProductMilestoneViewStructSubs(TestDistroViewStructSubs):
+class ProductMilestoneView(DistroView):
     """Test structural subscriptions on the product milestones."""
 
     def setUp(self):
-        super(TestProductMilestoneViewStructSubs, self).setUp()
+        BrowserTestCase.setUp(self)
         self.product = self.factory.makeProduct()
         with person_logged_in(self.product.owner):
             self.product.official_malone = True
@@ -507,7 +506,7 @@
 
     def test_subscribe_link_feature_flag_off_owner(self):
         self._create_scenario(self.product.owner, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_owner(self):
@@ -517,7 +516,7 @@
 
     def test_subscribe_link_feature_flag_off_user(self):
         self._create_scenario(self.regular_user, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     # There are no special bug supervisor rules for products.
@@ -528,7 +527,7 @@
 
     def test_subscribe_link_feature_flag_off_anonymous(self):
         self._create_scenario(ANONYMOUS, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_anonymous(self):
@@ -539,7 +538,7 @@
     def test_subscribe_link_feature_flag_off_admin(self):
         admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
         self._create_scenario(admin, None)
-        self.assertNotEqual(None, self.old_link)
+        self.assertOldLinkPresent()
         self.assertNewLinksMissing()
 
     def test_subscribe_link_feature_flag_on_admin(self):
@@ -550,12 +549,11 @@
         self.assertNewLinksPresent()
 
 
-class TestProductSeriesMilestoneViewStructSubs(
-    TestProductMilestoneViewStructSubs):
+class ProductSeriesMilestoneView(ProductMilestoneView):
     """Test structural subscriptions on the product series milestones."""
 
     def setUp(self):
-        super(TestProductSeriesMilestoneViewStructSubs, self).setUp()
+        super(ProductSeriesMilestoneView, self).setUp()
         self.productseries = self.factory.makeProductSeries()
         with person_logged_in(self.productseries.product.owner):
             self.productseries.product.official_malone = True
@@ -563,6 +561,293 @@
         self.target = self.factory.makeMilestone(
             productseries=self.productseries)
 
+# Tests for when the IStructuralSubscriptionTarget does not use Launchpad for
+# bug tracking.  In those cases the links should not be shown.
+
+class ProductDoesNotUseLPView(ProductView):
+    """Test structural subscriptions on the product view."""
+
+    def setUp(self):
+        super(ProductDoesNotUseLPView, self).setUp()
+        self.target = self.factory.makeProduct(official_malone=False)
+
+    def test_subscribe_link_feature_flag_on_owner(self):
+        # Test the new subscription link.
+        self._create_scenario(self.target.owner, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_user(self):
+        self._create_scenario(self.regular_user, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_anonymous(self):
+        self._create_scenario(ANONYMOUS, 'on')
+        # The subscribe link is not shown to anonymous.
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+
+class ProductDoesNotUseLPBugs(ProductDoesNotUseLPView):
+    """Test structural subscriptions on the product bugs view."""
+
+    rootsite = 'bugs'
+    view = '+bugs-index'
+
+    def test_subscribe_link_feature_flag_off_owner(self):
+        self._create_scenario(self.target.owner, None)
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_off_user(self):
+        self._create_scenario(self.regular_user, None)
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_off_anonymous(self):
+        self._create_scenario(ANONYMOUS, None)
+        # The old subscribe link is actually shown to anonymous users but the
+        # behavior has changed with the new link.
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+
+class ProjectGroupDoesNotUseLPView(ProductDoesNotUseLPView):
+    """Test structural subscriptions on the project group view."""
+
+    rootsite = None
+    view = '+index'
+
+    def setUp(self):
+        super(ProjectGroupDoesNotUseLPView, self).setUp()
+        self.target = self.factory.makeProject()
+        self.factory.makeProduct(
+            project=self.target, official_malone=False)
+
+
+class ProjectGroupDoesNotUseLPBugs(ProductDoesNotUseLPBugs):
+    """Test structural subscriptions on the project group bugs view."""
+
+    rootsite = 'bugs'
+    view = '+bugs'
+
+    def setUp(self):
+        super(ProjectGroupDoesNotUseLPBugs, self).setUp()
+        self.target = self.factory.makeProject()
+        self.factory.makeProduct(
+            project=self.target, official_malone=False)
+
+class ProductSeriesDoesNotUseLPView(ProductDoesNotUseLPView):
+    def setUp(self):
+        super(ProductSeriesDoesNotUseLPView, self).setUp()
+        product = self.factory.makeProduct(official_malone=False)
+        self.target = self.factory.makeProductSeries(product=product)
+
+
+class ProductSeriesDoesNotUseLPBugs(ProductDoesNotUseLPView):
+    def setUp(self):
+        super(ProductSeriesDoesNotUseLPBugs, self).setUp()
+        product = self.factory.makeProduct(official_malone=False)
+        self.target = self.factory.makeProductSeries(product=product)
+
+
+class DistributionSourcePackageDoesNotUseLPView(ProductDoesNotUseLPView):
+    """Test structural subscriptions on the distro src pkg view."""
+
+    def setUp(self):
+        super(DistributionSourcePackageDoesNotUseLPView, self).setUp()
+        distro = self.factory.makeDistribution()
+        self.target = self.factory.makeDistributionSourcePackage(
+            distribution=distro)
+        self.regular_user = self.factory.makePerson()
+
+    # DistributionSourcePackages do not have owners.
+    test_subscribe_link_feature_flag_off_owner = None
+    test_subscribe_link_feature_flag_on_owner = None
+
+
+class DistributionSourcePackageDoesNotUseLPBugs(ProductDoesNotUseLPBugs):
+    """Test structural subscriptions on the distro src pkg bugs view."""
+
+    view = '+bugs'
+
+    # DistributionSourcePackages do not have owners.
+    test_subscribe_link_feature_flag_off_owner = None
+    test_subscribe_link_feature_flag_on_owner = None
+
+
+class DistroDoesNotUseLPView(DistroView):
+    def setUp(self):
+        super(DistroDoesNotUseLPView, self).setUp()
+        self.target = self.factory.makeDistribution()
+        self.regular_user = self.factory.makePerson()
+
+    def test_subscribe_link_feature_flag_on_admin(self):
+        admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
+        self._create_scenario(admin, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_bug_super(self):
+        with celebrity_logged_in('admin'):
+            admin = getUtility(ILaunchBag).user
+            self.target.setBugSupervisor(
+                self.regular_user, admin)
+        self._create_scenario(self.regular_user, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_user_no_bug_super(self):
+        self._create_scenario(self.regular_user, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_owner(self):
+        # Test the new subscription link.
+        self._create_scenario(self.target.owner, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_user(self):
+        self._create_scenario(self.regular_user, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_anonymous(self):
+        self._create_scenario(ANONYMOUS, 'on')
+        # The subscribe link is not shown to anonymous.
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+
+class DistroDoesNotUseLPBugs(DistroDoesNotUseLPView):
+    rootsite = 'bugs'
+    view = '+bugs-index'
+
+    def test_subscribe_link_feature_flag_off_owner(self):
+        self._create_scenario(self.target.owner, None)
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_off_user(self):
+        self._create_scenario(self.regular_user, None)
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_off_anonymous(self):
+        self._create_scenario(ANONYMOUS, None)
+        # The old subscribe link is actually shown to anonymous users but the
+        # behavior has changed with the new link.
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+
+class DistroMilestoneDoesNotUseLPView(DistroMilestoneView):
+    def setUp(self):
+        super(DistroMilestoneDoesNotUseLPView, self).setUp()
+        with person_logged_in(self.distro.owner):
+            self.distro.official_malone = False
+
+    def test_subscribe_link_feature_flag_on_admin(self):
+        admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
+        self._create_scenario(admin, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_bug_super(self):
+        with celebrity_logged_in('admin'):
+            admin = getUtility(ILaunchBag).user
+            self.distro.setBugSupervisor(
+                self.regular_user, admin)
+        self._create_scenario(self.regular_user, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_user_no_bug_super(self):
+        self._create_scenario(self.regular_user, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_owner(self):
+        # Test the new subscription link.
+        self._create_scenario(self.distro.owner, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_user(self):
+        self._create_scenario(self.regular_user, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_anonymous(self):
+        self._create_scenario(ANONYMOUS, 'on')
+        # The subscribe link is not shown to anonymous.
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_user_with_bug_super(self):
+        with celebrity_logged_in('admin'):
+            admin = getUtility(ILaunchBag).user
+            supervisor = self.factory.makePerson()
+            self.distro.setBugSupervisor(
+                supervisor, admin)
+        self._create_scenario(self.regular_user, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+
+class ProductMilestoneDoesNotUseLPView(ProductMilestoneView):
+    def setUp(self):
+        BrowserTestCase.setUp(self)
+        self.product = self.factory.makeProduct()
+        with person_logged_in(self.product.owner):
+            self.product.official_malone = False
+        self.target = self.factory.makeMilestone(
+            name='1.0', product=self.product)
+        self.regular_user = self.factory.makePerson()
+
+    def test_subscribe_link_feature_flag_off_owner(self):
+        self._create_scenario(self.product.owner, None)
+        # The presence of the old link is certainly a mistake since the
+        # product does not use Launchpad for bug tracking.
+        self.assertOldLinkPresent()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_off_user(self):
+        self._create_scenario(self.regular_user, None)
+        # The presence of the old link is certainly a mistake since the
+        # product does not use Launchpad for bug tracking.
+        self.assertOldLinkPresent()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_off_anonymous(self):
+        self._create_scenario(ANONYMOUS, None)
+        # The presence of the old link is certainly a mistake since the
+        # product does not use Launchpad for bug tracking.
+        self.assertOldLinkPresent()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_off_admin(self):
+        admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
+        self._create_scenario(admin, None)
+        # The presence of the old link is certainly a mistake since the
+        # product does not use Launchpad for bug tracking.
+        self.assertOldLinkPresent()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_admin(self):
+        from lp.testing.sampledata import ADMIN_EMAIL
+        admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
+        self._create_scenario(admin, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
+    def test_subscribe_link_feature_flag_on_owner(self):
+        self._create_scenario(self.product.owner, 'on')
+        self.assertOldLinkMissing()
+        self.assertNewLinksMissing()
+
 
 def test_suite():
     """Return the `IStructuralSubscriptionTarget` TestSuite."""
@@ -570,20 +855,34 @@
     # Manually construct the test suite to avoid having tests from the base
     # class _TestStructSubs run.
     suite = unittest.TestSuite()
-    suite.addTest(unittest.makeSuite(TestProductViewStructSubs))
-    suite.addTest(unittest.makeSuite(TestProductBugsStructSubs))
-    suite.addTest(unittest.makeSuite(TestProductSeriesViewStructSubs))
-    suite.addTest(unittest.makeSuite(TestProductSeriesBugsStructSubs))
-    suite.addTest(unittest.makeSuite(TestProjectGroupViewStructSubs))
-    suite.addTest(unittest.makeSuite(TestProjectGroupBugsStructSubs))
-    suite.addTest(unittest.makeSuite(
-        TestDistributionSourcePackageViewStructSubs))
-    suite.addTest(unittest.makeSuite(
-        TestDistributionSourcePackageBugsStructSubs))
-    suite.addTest(unittest.makeSuite(TestDistroViewStructSubs))
-    suite.addTest(unittest.makeSuite(TestDistroBugsStructSubs))
-    suite.addTest(unittest.makeSuite(TestDistroMilestoneViewStructSubs))
-    suite.addTest(unittest.makeSuite(TestProductMilestoneViewStructSubs))
-    suite.addTest(unittest.makeSuite(
-        TestProductSeriesMilestoneViewStructSubs))
+    suite.addTest(unittest.makeSuite(ProductView))
+    suite.addTest(unittest.makeSuite(ProductBugs))
+    suite.addTest(unittest.makeSuite(ProductSeriesView))
+    suite.addTest(unittest.makeSuite(ProductSeriesBugs))
+    suite.addTest(unittest.makeSuite(ProjectGroupView))
+    suite.addTest(unittest.makeSuite(ProjectGroupBugs))
+    suite.addTest(unittest.makeSuite(DistributionSourcePackageView))
+    suite.addTest(unittest.makeSuite(DistributionSourcePackageBugs))
+    suite.addTest(unittest.makeSuite(DistroView))
+    suite.addTest(unittest.makeSuite(DistroBugs))
+    suite.addTest(unittest.makeSuite(DistroMilestoneView))
+    suite.addTest(unittest.makeSuite(ProductMilestoneView))
+    suite.addTest(unittest.makeSuite(ProductSeriesMilestoneView))
+
+    suite.addTest(unittest.makeSuite(ProductDoesNotUseLPView))
+    suite.addTest(unittest.makeSuite(ProductDoesNotUseLPBugs))
+    suite.addTest(unittest.makeSuite(ProductSeriesDoesNotUseLPView))
+    suite.addTest(unittest.makeSuite(ProductSeriesDoesNotUseLPBugs))
+    suite.addTest(unittest.makeSuite(ProjectGroupDoesNotUseLPView))
+    suite.addTest(unittest.makeSuite(ProjectGroupDoesNotUseLPBugs))
+    suite.addTest(unittest.makeSuite(
+        DistributionSourcePackageDoesNotUseLPView))
+    suite.addTest(unittest.makeSuite(
+        DistributionSourcePackageDoesNotUseLPBugs))
+    suite.addTest(unittest.makeSuite(DistroDoesNotUseLPView))
+    suite.addTest(unittest.makeSuite(DistroDoesNotUseLPBugs))
+    suite.addTest(unittest.makeSuite(
+        DistroMilestoneDoesNotUseLPView))
+    suite.addTest(unittest.makeSuite(ProductMilestoneDoesNotUseLPView))
+
     return suite

=== modified file 'lib/lp/registry/templates/distribution-index.pt'
--- lib/lp/registry/templates/distribution-index.pt	2011-04-11 00:34:52 +0000
+++ lib/lp/registry/templates/distribution-index.pt	2011-04-12 00:26:00 +0000
@@ -8,16 +8,19 @@
 
   <head>
     <tal:head-epilogue metal:fill-slot="head_epilogue">
-      <script type="text/javascript"
-        tal:condition="
-          request/features/malone.advanced-structural-subscriptions.enabled">
-          LPS.use('lp.registry.structural_subscription', function(Y) {
-              var module = Y.lp.registry.structural_subscription;
-              Y.on('domready', function() {
-                module.setup({content_box: "#structural-subscription-content-box"});
-              });
-          });
-      </script>
+      <tal:uses_launchpad_bugtracker
+         condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
+        <script type="text/javascript"
+          tal:condition="
+            request/features/malone.advanced-structural-subscriptions.enabled">
+            LPS.use('lp.registry.structural_subscription', function(Y) {
+                var module = Y.lp.registry.structural_subscription;
+                Y.on('domready', function() {
+                  module.setup({content_box: "#structural-subscription-content-box"});
+                });
+            });
+        </script>
+      </tal:uses_launchpad_bugtracker>
     </tal:head-epilogue>
   </head>
 

=== modified file 'lib/lp/registry/templates/distributionsourcepackage-index.pt'
--- lib/lp/registry/templates/distributionsourcepackage-index.pt	2011-04-05 16:05:15 +0000
+++ lib/lp/registry/templates/distributionsourcepackage-index.pt	2011-04-12 00:26:00 +0000
@@ -10,16 +10,19 @@
 <body>
 
 <metal:block fill-slot="head_epilogue">
-  <script type="text/javascript"
-      tal:condition="
-        request/features/malone.advanced-structural-subscriptions.enabled">
-    LPS.use('lp.registry.structural_subscription', function(Y) {
-        var module = Y.lp.registry.structural_subscription;
-        Y.on('domready', function() {
-          module.setup({content_box: "#structural-subscription-content-box"});
-        });
-    });
-  </script>
+  <tal:uses_launchpad_bugtracker
+     condition="context/distribution/bug_tracking_usage/enumvalue:LAUNCHPAD">
+    <script type="text/javascript"
+        tal:condition="
+          request/features/malone.advanced-structural-subscriptions.enabled">
+      LPS.use('lp.registry.structural_subscription', function(Y) {
+          var module = Y.lp.registry.structural_subscription;
+          Y.on('domready', function() {
+            module.setup({content_box: "#structural-subscription-content-box"});
+          });
+      });
+    </script>
+  </tal:uses_launchpad_bugtracker>
 </metal:block>
 
 <tal:side metal:fill-slot="side">

=== modified file 'lib/lp/registry/templates/distroseries-index.pt'
--- lib/lp/registry/templates/distroseries-index.pt	2011-03-30 21:38:03 +0000
+++ lib/lp/registry/templates/distroseries-index.pt	2011-04-12 00:26:00 +0000
@@ -13,16 +13,19 @@
       <script id="milestone-script" type="text/javascript"
         tal:condition="context/menu:overview/create_milestone/enabled"
         tal:content="view/register_milestone_script"></script>
-      <script type="text/javascript"
-        tal:condition="
-          request/features/malone.advanced-structural-subscriptions.enabled">
-          LPS.use('lp.registry.structural_subscription', function(Y) {
-              var module = Y.lp.registry.structural_subscription;
-              Y.on('domready', function() {
-                module.setup({content_box: "#structural-subscription-content-box"});
-              });
-          });
-      </script>
+      <tal:uses_launchpad_bugtracker
+         condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
+        <script type="text/javascript"
+          tal:condition="
+            request/features/malone.advanced-structural-subscriptions.enabled">
+            LPS.use('lp.registry.structural_subscription', function(Y) {
+                var module = Y.lp.registry.structural_subscription;
+                Y.on('domready', function() {
+                  module.setup({content_box: "#structural-subscription-content-box"});
+                });
+            });
+        </script>
+      </tal:uses_launchpad_bugtracker>
     </metal:block>
 
     <tal:heading metal:fill-slot="heading">

=== modified file 'lib/lp/registry/templates/milestone-index.pt'
--- lib/lp/registry/templates/milestone-index.pt	2011-03-29 22:34:04 +0000
+++ lib/lp/registry/templates/milestone-index.pt	2011-04-12 00:26:00 +0000
@@ -13,16 +13,19 @@
             background: #fff;
             }
     </style>
-    <script type="text/javascript"
-        tal:condition="
-          request/features/malone.advanced-structural-subscriptions.enabled">
-        LPS.use('lp.registry.structural_subscription', function(Y) {
-            var module = Y.lp.registry.structural_subscription;
-            Y.on('domready', function() {
-              module.setup({content_box: "#structural-subscription-content-box"});
-            });
-        });
-    </script>
+    <tal:uses_launchpad_bugtracker
+       condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
+      <script type="text/javascript"
+          tal:condition="
+            request/features/malone.advanced-structural-subscriptions.enabled">
+          LPS.use('lp.registry.structural_subscription', function(Y) {
+              var module = Y.lp.registry.structural_subscription;
+              Y.on('domready', function() {
+                module.setup({content_box: "#structural-subscription-content-box"});
+              });
+          });
+      </script>
+    </tal:uses_launchpad_bugtracker>
   </tal:head-epilogue>
 
   <body>

=== modified file 'lib/lp/registry/templates/product-index.pt'
--- lib/lp/registry/templates/product-index.pt	2011-03-29 22:34:04 +0000
+++ lib/lp/registry/templates/product-index.pt	2011-04-12 00:26:00 +0000
@@ -31,17 +31,19 @@
         div.collapsible, div.collapsed {display: block;}
       </style>
     </noscript>
-
-    <script type="text/javascript"
-        tal:condition="
-          request/features/malone.advanced-structural-subscriptions.enabled">
-        LPS.use('lp.registry.structural_subscription', function(Y) {
-            var module = Y.lp.registry.structural_subscription;
-            Y.on('domready', function() {
-              module.setup({content_box: "#structural-subscription-content-box"});
-            });
-        });
-    </script>
+    <tal:uses_launchpad_bugtracker
+       condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
+      <script type="text/javascript"
+          tal:condition="
+            request/features/malone.advanced-structural-subscriptions.enabled">
+          LPS.use('lp.registry.structural_subscription', function(Y) {
+              var module = Y.lp.registry.structural_subscription;
+              Y.on('domready', function() {
+                module.setup({content_box: "#structural-subscription-content-box"});
+              });
+          });
+      </script>
+    </tal:uses_launchpad_bugtracker>
   </tal:head-epilogue>
 </head>
 

=== modified file 'lib/lp/registry/templates/productseries-index.pt'
--- lib/lp/registry/templates/productseries-index.pt	2011-04-04 18:51:17 +0000
+++ lib/lp/registry/templates/productseries-index.pt	2011-04-12 00:26:00 +0000
@@ -14,16 +14,19 @@
     <script id="milestone-script" type="text/javascript"
       tal:condition="context/menu:overview/create_milestone/enabled"
       tal:content="view/register_milestone_script"></script>
-    <script type="text/javascript"
-        tal:condition="
-          request/features/malone.advanced-structural-subscriptions.enabled">
-        LPS.use('lp.registry.structural_subscription', function(Y) {
-            var module = Y.lp.registry.structural_subscription;
-            Y.on('domready', function() {
-              module.setup({content_box: "#structural-subscription-content-box"});
-            });
-        });
-    </script>
+    <tal:uses_launchpad_bugtracker
+       condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
+      <script type="text/javascript"
+          tal:condition="
+            request/features/malone.advanced-structural-subscriptions.enabled">
+          LPS.use('lp.registry.structural_subscription', function(Y) {
+              var module = Y.lp.registry.structural_subscription;
+              Y.on('domready', function() {
+                module.setup({content_box: "#structural-subscription-content-box"});
+              });
+          });
+      </script>
+    </tal:uses_launchpad_bugtracker>
   </metal:block>
 
     <tal:heading metal:fill-slot="heading">

=== modified file 'lib/lp/registry/templates/project-index.pt'
--- lib/lp/registry/templates/project-index.pt	2011-03-29 22:34:04 +0000
+++ lib/lp/registry/templates/project-index.pt	2011-04-12 00:26:00 +0000
@@ -9,16 +9,19 @@
 
   <head>
     <tal:head-epilogue metal:fill-slot="head_epilogue">
-      <script type="text/javascript"
-          tal:condition="
-            request/features/malone.advanced-structural-subscriptions.enabled">
-          LPS.use('lp.registry.structural_subscription', function(Y) {
-              var module = Y.lp.registry.structural_subscription;
-              Y.on('domready', function() {
-                module.setup({content_box: "#structural-subscription-content-box"});
-              });
-          });
-      </script>
+      <tal:uses_launchpad_bugtracker
+         condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
+        <script type="text/javascript"
+            tal:condition="
+              request/features/malone.advanced-structural-subscriptions.enabled">
+            LPS.use('lp.registry.structural_subscription', function(Y) {
+                var module = Y.lp.registry.structural_subscription;
+                Y.on('domready', function() {
+                  module.setup({content_box: "#structural-subscription-content-box"});
+                });
+            });
+        </script>
+      </tal:uses_launchpad_bugtracker>
     </tal:head-epilogue>
   </head>