← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~benji/launchpad/bug-784575-flags into lp:launchpad

 

Benji York has proposed merging lp:~benji/launchpad/bug-784575-flags into lp:launchpad with lp:~benji/launchpad/bug-784575-message as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~benji/launchpad/bug-784575-flags/+merge/62175

This branch removes the two feature flags used while developing the
better subscription story features.

This branch is large because many of the changes involve removing if
statements that check whether or not the feature flags are enabled and
dedenting the code inside the if block.  For the most part these large
blocks were left unchanged, so as to expedite review.

Because of the far-reaching effects of removing these feature flags, no
small set of tests sufficiently cover this change.  I've succesfully run
"bin/test -m lp.bugs" and the post-review EC2 test run should shake out
any remaining test failures.

The make lint report shows no remaining lint.  Only a small amount of
pre-existing lint had to be fixed while working on this branch.

-- 
https://code.launchpad.net/~benji/launchpad/bug-784575-flags/+merge/62175
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~benji/launchpad/bug-784575-flags into lp:launchpad.
=== modified file 'lib/lp/bugs/browser/bug.py'
--- lib/lp/bugs/browser/bug.py	2011-05-17 14:54:56 +0000
+++ lib/lp/bugs/browser/bug.py	2011-05-24 18:03:27 +0000
@@ -102,7 +102,6 @@
 from lp.bugs.interfaces.bugwatch import IBugWatchSet
 from lp.bugs.interfaces.cve import ICveSet
 from lp.bugs.mail.bugnotificationbuilder import format_rfc2822_date
-from lp.services import features
 from lp.services.fields import DuplicateBug
 from lp.services.propertycache import cachedproperty
 
@@ -210,11 +209,6 @@
         ContextMenu.__init__(self, getUtility(ILaunchBag).bugtask)
 
     @cachedproperty
-    def _use_advanced_features(self):
-        """Return True if advanced subscriptions features are enabled."""
-        return features.getFeatureFlag(
-            'malone.advanced-subscriptions.enabled')
-
     def editdescription(self):
         """Return the 'Edit description/tags' Link."""
         text = 'Update description / tags'
@@ -248,16 +242,12 @@
         elif user is not None and (
             self.context.bug.isSubscribed(user) or
             self.context.bug.isSubscribedToDupes(user)):
-            if self._use_advanced_features:
-                if self.context.bug.isMuted(user):
-                    text = 'Subscribe'
-                    icon = 'add'
-                else:
-                    text = 'Edit subscription'
-                    icon = 'edit'
+            if self.context.bug.isMuted(user):
+                text = 'Subscribe'
+                icon = 'add'
             else:
-                text = 'Unsubscribe'
-                icon = 'remove'
+                text = 'Edit subscription'
+                icon = 'edit'
         else:
             text = 'Subscribe'
             icon = 'add'
@@ -536,17 +526,14 @@
     @cachedproperty
     def user_should_see_mute_link(self):
         """Return True if the user should see the Mute link."""
-        if features.getFeatureFlag('malone.advanced-subscriptions.enabled'):
-            user_is_subscribed = (
-                # Note that we don't have to check for isMuted(), since
-                # if isMuted() is True isSubscribed() will also be
-                # True.
-                self.context.isSubscribed(self.user) or
-                self.context.isSubscribedToDupes(self.user) or
-                self.context.personIsAlsoNotifiedSubscriber(self.user))
-            return user_is_subscribed
-        else:
-            return False
+        return (
+            # Note that we don't have to check for isMuted(), since
+            # if isMuted() is True isSubscribed() will also be
+            # True.
+            self.user is not None and
+            (self.context.isSubscribed(self.user) or
+             self.context.isSubscribedToDupes(self.user) or
+             self.context.personIsAlsoNotifiedSubscriber(self.user)))
 
     @cachedproperty
     def _bug_attachments(self):

=== modified file 'lib/lp/bugs/browser/bugsubscription.py'
--- lib/lp/bugs/browser/bugsubscription.py	2011-05-12 14:59:21 +0000
+++ lib/lp/bugs/browser/bugsubscription.py	2011-05-24 18:03:27 +0000
@@ -49,7 +49,6 @@
 from lp.bugs.model.structuralsubscription import (
     get_structural_subscriptions_for_bug,
     )
-from lp.services import features
 from lp.services.propertycache import cachedproperty
 
 
@@ -106,12 +105,6 @@
     """
 
     @cachedproperty
-    def _use_advanced_features(self):
-        """Return True if advanced subscriptions features are enabled."""
-        return features.getFeatureFlag(
-            'malone.advanced-subscriptions.enabled')
-
-    @cachedproperty
     def _bug_notification_level_field(self):
         """Return a custom form field for bug_notification_level."""
         # We rebuild the items that we show in the field so that the
@@ -145,10 +138,6 @@
 
     def _setUpBugNotificationLevelField(self):
         """Set up the bug_notification_level field."""
-        if not self._use_advanced_features:
-            # If advanced features are disabled, do nothing.
-            return
-
         self.form_fields = self.form_fields.omit('bug_notification_level')
         self.form_fields += formlib.form.Fields(
             self._bug_notification_level_field)
@@ -177,10 +166,7 @@
 
     @property
     def field_names(self):
-        if self._use_advanced_features:
-            return ['bug_notification_level']
-        else:
-            return []
+        return ['bug_notification_level']
 
     @property
     def next_url(self):
@@ -240,7 +226,7 @@
 
     @cachedproperty
     def _unsubscribe_current_user_term(self):
-        if self._use_advanced_features and self.user_is_muted:
+        if self.user_is_muted:
             label = "unmute bug mail from this bug"
         else:
             label = 'unsubscribe me from this bug'
@@ -252,11 +238,9 @@
         self_subscribed = False
         for person in self._subscribers_for_current_user:
             if person.id == self.user.id:
-                if (self._use_advanced_features and
-                    (self.user_is_subscribed_directly or
-                    self.user_is_muted)):
-                        subscription_terms.append(
-                            self._update_subscription_term)
+                if (self.user_is_subscribed_directly or self.user_is_muted):
+                    subscription_terms.append(
+                        self._update_subscription_term)
                 subscription_terms.insert(
                     0, self._unsubscribe_current_user_term)
                 self_subscribed = True
@@ -280,8 +264,7 @@
             subscription_terms[-1].title += '.'
 
         subscription_vocabulary = SimpleVocabulary(subscription_terms)
-        if (self._use_advanced_features and
-            self.user_is_subscribed_directly):
+        if self.user_is_subscribed_directly:
             default_subscription_value = self._update_subscription_term.value
         else:
             default_subscription_value = (
@@ -308,26 +291,25 @@
         """See `LaunchpadFormView`."""
         super(BugSubscriptionSubscribeSelfView, self).setUpWidgets()
         self.widgets['subscription'].widget_class = 'bug-subscription-basic'
-        if self._use_advanced_features:
-            self.widgets['bug_notification_level'].widget_class = (
-                'bug-notification-level-field')
-            if self._subscriber_count_for_current_user == 0:
-                # We hide the subscription widget if the user isn't
-                # subscribed, since we know who the subscriber is and we
-                # don't need to present them with a single radio button.
-                self.widgets['subscription'].visible = False
-            else:
-                # We show the subscription widget when the user is
-                # subscribed via a team, because they can either
-                # subscribe theirself or unsubscribe their team.
-                self.widgets['subscription'].visible = True
+        self.widgets['bug_notification_level'].widget_class = (
+            'bug-notification-level-field')
+        if self._subscriber_count_for_current_user == 0:
+            # We hide the subscription widget if the user isn't
+            # subscribed, since we know who the subscriber is and we
+            # don't need to present them with a single radio button.
+            self.widgets['subscription'].visible = False
+        else:
+            # We show the subscription widget when the user is
+            # subscribed via a team, because they can either
+            # subscribe theirself or unsubscribe their team.
+            self.widgets['subscription'].visible = True
 
-            if (self.user_is_subscribed and
-                self.user_is_subscribed_to_dupes_only):
-                # If the user is subscribed via a duplicate but is not
-                # directly subscribed, we hide the
-                # bug_notification_level field, since it's not used.
-                self.widgets['bug_notification_level'].visible = False
+        if (self.user_is_subscribed and
+            self.user_is_subscribed_to_dupes_only):
+            # If the user is subscribed via a duplicate but is not
+            # directly subscribed, we hide the
+            # bug_notification_level field, since it's not used.
+            self.widgets['bug_notification_level'].visible = False
 
     @cachedproperty
     def user_is_muted(self):
@@ -381,10 +363,7 @@
     def subscribe_action(self, action, data):
         """Handle subscription requests."""
         subscription_person = self.widgets['subscription'].getInputValue()
-        if self._use_advanced_features:
-            bug_notification_level = data.get('bug_notification_level', None)
-        else:
-            bug_notification_level = None
+        bug_notification_level = data.get('bug_notification_level', None)
 
         if (subscription_person == self._update_subscription_term.value and
             (self.user_is_subscribed or self.user_is_muted)):

=== modified file 'lib/lp/bugs/browser/structuralsubscription.py'
--- lib/lp/bugs/browser/structuralsubscription.py	2011-05-17 15:41:09 +0000
+++ lib/lp/bugs/browser/structuralsubscription.py	2011-05-24 18:03:27 +0000
@@ -396,7 +396,10 @@
     @enabled_with_permission('launchpad.AnyPerson')
     def subscribe_to_bug_mail(self):
         text = 'Subscribe to bug mail'
-        return Link('#', text, icon='add', hidden=True, enabled=self._enabled)
+        # Clicks to this link will be intercepted by the on-page JavaScript,
+        # but we want a link target for non-JS-enabled browsers.
+        return Link('+subscribe', text, icon='add', hidden=True,
+            enabled=self._enabled)
 
     @enabled_with_permission('launchpad.AnyPerson')
     def edit_bug_mail(self):

=== modified file 'lib/lp/bugs/browser/tests/bug-views.txt'
--- lib/lp/bugs/browser/tests/bug-views.txt	2011-04-12 05:24:16 +0000
+++ lib/lp/bugs/browser/tests/bug-views.txt	2011-05-24 18:03:27 +0000
@@ -166,7 +166,7 @@
     3
     >>> [ (c.index, c.owner.name, c.text_contents)
     ...  for c in ubuntu_bugview.comments ]
-    [(0, u'name16', u'Binary package hint: linux-2.6.12\n\na bug in a bin pkg'),
+    [(0, u'name16', u'Binary package hint: linux-2.6.12...'),
      (1, u'name16', u'I can reproduce this bug.'),
      (2, u'name16', u'I can reproduce this bug.')]
 
@@ -208,7 +208,7 @@
 The displayable comments for a bug can be obtained from the view
 property activity_and_comments.
 
-    >>> comments = [event.get('comment') for event in 
+    >>> comments = [event.get('comment') for event in
     ...     ubuntu_bugview.activity_and_comments if event.get('comment')]
 
 Because we omit the first comment, and because the third comment is
@@ -346,10 +346,10 @@
     <lp.bugs.model.bugsubscription.BugSubscription ...>
     >>> bug_menu = BugContextMenu(bug_one_bugtask)
     >>> bug_menu.subscription().text
-    'Unsubscribe'
+    'Edit subscription'
 
     >>> bug_menu.subscription().icon
-    'remove'
+    'edit'
 
 If we subscribe one of the teams that Foo Bar is a member of, it will
 still say 'Unsubscribe':
@@ -361,10 +361,10 @@
     <lp.bugs.model.bugsubscription.BugSubscription ...>
     >>> bug_menu = BugContextMenu(bug_one_bugtask)
     >>> bug_menu.subscription().text
-    'Unsubscribe'
+    'Edit subscription'
 
     >>> bug_menu.subscription().icon
-    'remove'
+    'edit'
 
 If we now unsubscribe Foo Bar, it will say 'Subscribe', since team
 unsubsription is handled by the remove icon next the the team in the
@@ -414,7 +414,7 @@
     >>> syncUpdate(bug_two)
 
     >>> bug_menu.subscription().text
-        'Unsubscribe'
+    'Edit subscription'
 
     Now, let's revert that duplicate marking and demonstrate it again, this
     time where the subscription from the duplicate is of a /team/ of which

=== modified file 'lib/lp/bugs/browser/tests/test_bug_context_menu.py'
--- lib/lp/bugs/browser/tests/test_bug_context_menu.py	2011-04-13 18:03:03 +0000
+++ lib/lp/bugs/browser/tests/test_bug_context_menu.py	2011-05-24 18:03:27 +0000
@@ -17,7 +17,6 @@
 from lp.testing import (
     feature_flags,
     person_logged_in,
-    set_feature_flag,
     TestCaseWithFactory,
     )
 from lp.testing.views import create_initialized_view
@@ -37,8 +36,6 @@
         launchbag.add(self.bug)
         launchbag.add(self.bug.default_bugtask)
         self.context_menu = BugContextMenu(self.bug)
-        with feature_flags():
-            set_feature_flag(u'malone.advanced-subscriptions.enabled', u'on')
 
     def test_text_for_muted_subscriptions(self):
         # If a user has a mute on a bug it's recorded internally as a

=== modified file 'lib/lp/bugs/browser/tests/test_bug_views.py'
--- lib/lp/bugs/browser/tests/test_bug_views.py	2011-05-24 08:33:59 +0000
+++ lib/lp/bugs/browser/tests/test_bug_views.py	2011-05-24 18:03:27 +0000
@@ -15,7 +15,6 @@
 from canonical.launchpad.testing.pages import find_tag_by_id
 from canonical.testing.layers import DatabaseFunctionalLayer
 
-from lp.services.features.testing import FeatureFixture
 from lp.services.features import get_relevant_feature_controller
 from lp.testing import (
     BrowserTestCase,
@@ -77,8 +76,6 @@
 class TestBugPortletSubscribers(TestCaseWithFactory):
 
     layer = DatabaseFunctionalLayer
-    feature_flag_1 = 'malone.advanced-subscriptions.enabled'
-    feature_flag_2 = 'malone.advanced-structural-subscriptions.enabled'
 
     def setUp(self):
         super(TestBugPortletSubscribers, self).setUp()
@@ -93,33 +90,14 @@
         launchbag.add(self.bug.default_bugtask)
 
     def test_edit_subscriptions_link_shown_when_feature_enabled(self):
-        with FeatureFixture({self.feature_flag_2: 'on'}):
-            request = LaunchpadTestRequest()
-            request.features = get_relevant_feature_controller()
-            view = create_initialized_view(
-                self.bug, name="+portlet-subscribers", request=request)
-            html = view.render()
+        request = LaunchpadTestRequest()
+        request.features = get_relevant_feature_controller()
+        view = create_initialized_view(
+            self.bug, name="+portlet-subscribers", request=request)
+        html = view.render()
         self.assertTrue('menu-link-editsubscriptions' in html)
         self.assertTrue('/+subscriptions' in html)
 
-    def test_edit_subscriptions_link_not_shown_when_feature_disabled(self):
-        view = create_initialized_view(
-            self.bug, name="+portlet-subscribers")
-        html = view.render()
-        self.assertTrue('menu-link-editsubscriptions' not in html)
-        self.assertTrue('/+subscriptions' not in html)
-
-    def test_mute_subscription_link_not_shown_with_no_feature_flag(self):
-        # Mute link is not shown when the feature flag is off.
-        person = self.factory.makePerson()
-        with person_logged_in(person):
-            with FeatureFixture({self.feature_flag_1: None}):
-                view = create_initialized_view(
-                    self.bug, name="+portlet-subscribers")
-                self.assertFalse(view.user_should_see_mute_link)
-                html = view.render()
-                self.assertFalse('mute_subscription' in html)
-
     def _hasCSSClass(self, html, element_id, css_class):
         # Return True if element with ID `element_id` in `html` has
         # a CSS class `css_class`.
@@ -131,24 +109,23 @@
         # If the person has a structural subscription to the pillar,
         # then the mute link will be displayed to them.
         person = self.factory.makePerson(name="a-person")
-        with FeatureFixture({self.feature_flag_1: 'on'}):
-            with person_logged_in(person):
-                self.target.addBugSubscription(person, person)
-                self.assertFalse(self.bug.isMuted(person))
-                view = create_initialized_view(
-                    self.bug, name="+portlet-subscribers")
-                self.assertTrue(view.user_should_see_mute_link,
-                                "User should see mute link.")
-                contents = view.render()
-                self.assertTrue('mute_subscription' in contents,
-                                "'mute_subscription' not in contents.")
-                self.assertFalse(
-                    self._hasCSSClass(
-                        contents, 'mute-link-container', 'hidden'))
-                create_initialized_view(
-                    self.bug.default_bugtask, name="+mute",
-                    form={'field.actions.mute': 'Mute bug mail'})
-                self.assertTrue(self.bug.isMuted(person))
+        with person_logged_in(person):
+            self.target.addBugSubscription(person, person)
+            self.assertFalse(self.bug.isMuted(person))
+            view = create_initialized_view(
+                self.bug, name="+portlet-subscribers")
+            self.assertTrue(view.user_should_see_mute_link,
+                            "User should see mute link.")
+            contents = view.render()
+            self.assertTrue('mute_subscription' in contents,
+                            "'mute_subscription' not in contents.")
+            self.assertFalse(
+                self._hasCSSClass(
+                    contents, 'mute-link-container', 'hidden'))
+            create_initialized_view(
+                self.bug.default_bugtask, name="+mute",
+                form={'field.actions.mute': 'Mute bug mail'})
+            self.assertTrue(self.bug.isMuted(person))
 
     def test_mute_subscription_link_shown_for_team_subscription(self):
         # If the person belongs to a team with a structural subscription,
@@ -156,35 +133,35 @@
         person = self.factory.makePerson(name="a-person")
         team_owner = self.factory.makePerson(name="team-owner")
         team = self.factory.makeTeam(owner=team_owner, name="subscribed-team")
-        with FeatureFixture({self.feature_flag_1: 'on'}):
-            with person_logged_in(team_owner):
-                team.addMember(person, team_owner)
-                self.target.addBugSubscription(team, team_owner)
-            with person_logged_in(person):
-                self.assertFalse(self.bug.isMuted(person))
-                self.assertTrue(
-                    self.bug.personIsAlsoNotifiedSubscriber(
-                        person), "Person should be a notified subscriber")
-                view = create_initialized_view(
-                    self.bug, name="+portlet-subscribers")
-                self.assertTrue(view.user_should_see_mute_link,
-                                "User should see mute link.")
-                contents = view.render()
-                self.assertTrue('mute_subscription' in contents,
-                                "'mute_subscription' not in contents.")
-                self.assertFalse(
-                    self._hasCSSClass(
-                        contents, 'mute-link-container', 'hidden'))
-                create_initialized_view(
-                    self.bug.default_bugtask, name="+mute",
-                    form={'field.actions.mute': 'Mute bug mail'})
-                self.assertTrue(self.bug.isMuted(person))
+        with person_logged_in(team_owner):
+            team.addMember(person, team_owner)
+            self.target.addBugSubscription(team, team_owner)
+        with person_logged_in(person):
+            self.assertFalse(self.bug.isMuted(person))
+            self.assertTrue(
+                self.bug.personIsAlsoNotifiedSubscriber(
+                    person), "Person should be a notified subscriber")
+            view = create_initialized_view(
+                self.bug, name="+portlet-subscribers")
+            self.assertTrue(view.user_should_see_mute_link,
+                            "User should see mute link.")
+            contents = view.render()
+            self.assertTrue('mute_subscription' in contents,
+                            "'mute_subscription' not in contents.")
+            self.assertFalse(
+                self._hasCSSClass(
+                    contents, 'mute-link-container', 'hidden'))
+            create_initialized_view(
+                self.bug.default_bugtask, name="+mute",
+                form={'field.actions.mute': 'Mute bug mail'})
+            self.assertTrue(self.bug.isMuted(person))
 
     def test_mute_subscription_link_hidden_for_non_subscribers(self):
         # If a person is not already subscribed to a bug in some way,
         # the mute link will not be displayed to them.
         person = self.factory.makePerson()
         with person_logged_in(person):
+<<<<<<< TREE
             with FeatureFixture({self.feature_flag_1: 'on'}):
                 # The user isn't subscribed or muted already.
                 self.assertFalse(self.bug.isSubscribed(person))
@@ -202,3 +179,21 @@
                 self.assertTrue(
                     self._hasCSSClass(html, 'mute-link-container', 'hidden'),
                     'No "hidden" CSS class in mute-link-container.')
+=======
+            # The user isn't subscribed or muted already.
+            self.assertFalse(self.bug.isSubscribed(person))
+            self.assertFalse(self.bug.isMuted(person))
+            self.assertFalse(
+                self.bug.personIsAlsoNotifiedSubscriber(
+                    person))
+            view = create_initialized_view(
+                self.bug, name="+portlet-subscribers")
+            self.assertFalse(view.user_should_see_mute_link)
+            html = view.render()
+            self.assertTrue('mute_subscription' in html)
+            # The template uses user_should_see_mute_link to decide
+            # whether or not to display the mute link.
+            self.assertTrue(
+                self._hasCSSClass(html, 'mute-link-container', 'hidden'),
+                'No "hidden" CSS class in mute-link-container.')
+>>>>>>> MERGE-SOURCE

=== modified file 'lib/lp/bugs/browser/tests/test_bugsubscription_views.py'
--- lib/lp/bugs/browser/tests/test_bugsubscription_views.py	2011-04-26 11:58:26 +0000
+++ lib/lp/bugs/browser/tests/test_bugsubscription_views.py	2011-05-24 18:03:27 +0000
@@ -15,7 +15,6 @@
     BugSubscriptionSubscribeSelfView,
     )
 from lp.bugs.enum import BugNotificationLevel
-from lp.services.features.testing import FeatureFixture
 from lp.testing import (
     person_logged_in,
     TestCaseWithFactory,
@@ -30,7 +29,6 @@
 class BugSubscriptionAdvancedFeaturesTestCase(TestCaseWithFactory):
 
     layer = LaunchpadFunctionalLayer
-    feature_flag = 'malone.advanced-subscriptions.enabled'
 
     def setUp(self):
         super(BugSubscriptionAdvancedFeaturesTestCase, self).setUp()
@@ -49,70 +47,67 @@
 
         # We don't display BugNotificationLevel.NOTHING as an option.
         # This is tested below.
-        with FeatureFixture({self.feature_flag: ON}):
-            displayed_levels = [
-                level for level in BugNotificationLevel.items
-                if level != BugNotificationLevel.NOTHING]
-            for level in displayed_levels:
-                person = self.factory.makePerson()
-                with person_logged_in(person):
-                    harness = LaunchpadFormHarness(
-                        bug.default_bugtask, BugSubscriptionSubscribeSelfView)
-                    form_data = {
-                        'field.subscription': person.name,
-                        'field.bug_notification_level': level.title,
-                        }
-                    harness.submit('continue', form_data)
+        displayed_levels = [
+            level for level in BugNotificationLevel.items
+            if level != BugNotificationLevel.NOTHING]
+        for level in displayed_levels:
+            person = self.factory.makePerson()
+            with person_logged_in(person):
+                harness = LaunchpadFormHarness(
+                    bug.default_bugtask, BugSubscriptionSubscribeSelfView)
+                form_data = {
+                    'field.subscription': person.name,
+                    'field.bug_notification_level': level.title,
+                    }
+                harness.submit('continue', form_data)
 
-                subscription = bug.getSubscriptionForPerson(person)
-                self.assertEqual(
-                    level, subscription.bug_notification_level,
-                    "Bug notification level of subscription should be %s, is "
-                    "actually %s." % (
-                        level.title,
-                        subscription.bug_notification_level.title))
+            subscription = bug.getSubscriptionForPerson(person)
+            self.assertEqual(
+                level, subscription.bug_notification_level,
+                "Bug notification level of subscription should be %s, is "
+                "actually %s." % (
+                    level.title,
+                    subscription.bug_notification_level.title))
 
     def test_nothing_is_not_a_valid_level(self):
         # BugNotificationLevel.NOTHING isn't considered valid when
         # someone is trying to subscribe.
         bug = self.factory.makeBug()
         person = self.factory.makePerson()
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(person):
-                level = BugNotificationLevel.NOTHING
-                harness = LaunchpadFormHarness(
-                    bug.default_bugtask, BugSubscriptionSubscribeSelfView)
-                form_data = {
-                    'field.subscription': person.name,
-                    'field.bug_notification_level': level.title,
-                    }
-                harness.submit('continue', form_data)
-                self.assertTrue(harness.hasErrors())
-                self.assertEqual(
-                    'Invalid value',
-                    harness.getFieldError('bug_notification_level'),
-                    "The view should treat BugNotificationLevel.NOTHING "
-                    "as an invalid value.")
+        with person_logged_in(person):
+            level = BugNotificationLevel.NOTHING
+            harness = LaunchpadFormHarness(
+                bug.default_bugtask, BugSubscriptionSubscribeSelfView)
+            form_data = {
+                'field.subscription': person.name,
+                'field.bug_notification_level': level.title,
+                }
+            harness.submit('continue', form_data)
+            self.assertTrue(harness.hasErrors())
+            self.assertEqual(
+                'Invalid value',
+                harness.getFieldError('bug_notification_level'),
+                "The view should treat BugNotificationLevel.NOTHING "
+                "as an invalid value.")
 
     def test_user_can_update_subscription(self):
         # A user can update their bug subscription using the
         # BugSubscriptionSubscribeSelfView.
         bug = self.factory.makeBug()
         person = self.factory.makePerson()
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(person):
-                bug.subscribe(person, person, BugNotificationLevel.COMMENTS)
-                # Now the person updates their subscription so they're
-                # subscribed at the METADATA level.
-                level = BugNotificationLevel.METADATA
-                harness = LaunchpadFormHarness(
-                    bug.default_bugtask, BugSubscriptionSubscribeSelfView)
-                form_data = {
-                    'field.subscription': 'update-subscription',
-                    'field.bug_notification_level': level.title,
-                    }
-                harness.submit('continue', form_data)
-                self.assertFalse(harness.hasErrors())
+        with person_logged_in(person):
+            bug.subscribe(person, person, BugNotificationLevel.COMMENTS)
+            # Now the person updates their subscription so they're
+            # subscribed at the METADATA level.
+            level = BugNotificationLevel.METADATA
+            harness = LaunchpadFormHarness(
+                bug.default_bugtask, BugSubscriptionSubscribeSelfView)
+            form_data = {
+                'field.subscription': 'update-subscription',
+                'field.bug_notification_level': level.title,
+                }
+            harness.submit('continue', form_data)
+            self.assertFalse(harness.hasErrors())
 
         subscription = bug.getSubscriptionForPerson(person)
         self.assertEqual(
@@ -126,15 +121,14 @@
         # BugSubscriptionSubscribeSelfView.
         bug = self.factory.makeBug()
         person = self.factory.makePerson()
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(person):
-                bug.subscribe(person, person)
-                harness = LaunchpadFormHarness(
-                    bug.default_bugtask, BugSubscriptionSubscribeSelfView)
-                form_data = {
-                    'field.subscription': person.name,
-                    }
-                harness.submit('continue', form_data)
+        with person_logged_in(person):
+            bug.subscribe(person, person)
+            harness = LaunchpadFormHarness(
+                bug.default_bugtask, BugSubscriptionSubscribeSelfView)
+            form_data = {
+                'field.subscription': person.name,
+                }
+            harness.submit('continue', form_data)
 
         subscription = bug.getSubscriptionForPerson(person)
         self.assertIs(
@@ -148,35 +142,34 @@
         # level.
         bug = self.factory.makeBug()
         person = self.factory.makePerson()
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(person):
-                # We subscribe using the harness rather than doing it
-                # directly so that we don't have to commit() between
-                # subscribing and checking the default value.
-                level = BugNotificationLevel.METADATA
-                harness = LaunchpadFormHarness(
-                    bug.default_bugtask, BugSubscriptionSubscribeSelfView)
-                form_data = {
-                    'field.subscription': person.name,
-                    'field.bug_notification_level': level.title,
-                    }
-                harness.submit('continue', form_data)
+        with person_logged_in(person):
+            # We subscribe using the harness rather than doing it
+            # directly so that we don't have to commit() between
+            # subscribing and checking the default value.
+            level = BugNotificationLevel.METADATA
+            harness = LaunchpadFormHarness(
+                bug.default_bugtask, BugSubscriptionSubscribeSelfView)
+            form_data = {
+                'field.subscription': person.name,
+                'field.bug_notification_level': level.title,
+                }
+            harness.submit('continue', form_data)
 
-                # The default value for the bug_notification_level field
-                # should now be the same as the level used to subscribe
-                # above.
-                harness = LaunchpadFormHarness(
-                    bug.default_bugtask, BugSubscriptionSubscribeSelfView)
-                bug_notification_level_widget = (
-                    harness.view.widgets['bug_notification_level'])
-                default_notification_level_value = (
-                    bug_notification_level_widget._getDefault())
-                self.assertEqual(
-                    BugNotificationLevel.METADATA,
-                    default_notification_level_value,
-                    "Default value for bug_notification_level should be "
-                    "METADATA, is actually %s"
-                    % default_notification_level_value)
+            # The default value for the bug_notification_level field
+            # should now be the same as the level used to subscribe
+            # above.
+            harness = LaunchpadFormHarness(
+                bug.default_bugtask, BugSubscriptionSubscribeSelfView)
+            bug_notification_level_widget = (
+                harness.view.widgets['bug_notification_level'])
+            default_notification_level_value = (
+                bug_notification_level_widget._getDefault())
+            self.assertEqual(
+                BugNotificationLevel.METADATA,
+                default_notification_level_value,
+                "Default value for bug_notification_level should be "
+                "METADATA, is actually %s"
+                % default_notification_level_value)
 
     def test_update_subscription_fails_if_user_not_subscribed(self):
         # If the user is not directly subscribed to the bug, trying to
@@ -184,16 +177,15 @@
         # subscription that doesn't exist).
         bug = self.factory.makeBug()
         person = self.factory.makePerson()
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(person):
-                harness = LaunchpadFormHarness(
-                    bug.default_bugtask, BugSubscriptionSubscribeSelfView)
-                subscription_field = (
-                    harness.view.form_fields['subscription'].field)
-                # The update-subscription option won't appear.
-                self.assertNotIn(
-                    'update-subscription',
-                    subscription_field.vocabulary.by_token)
+        with person_logged_in(person):
+            harness = LaunchpadFormHarness(
+                bug.default_bugtask, BugSubscriptionSubscribeSelfView)
+            subscription_field = (
+                harness.view.form_fields['subscription'].field)
+            # The update-subscription option won't appear.
+            self.assertNotIn(
+                'update-subscription',
+                subscription_field.vocabulary.by_token)
 
     def test_update_subscription_fails_for_users_subscribed_via_teams(self):
         # If the user is not directly subscribed, but is subscribed via
@@ -202,17 +194,16 @@
         bug = self.factory.makeBug()
         person = self.factory.makePerson()
         team = self.factory.makeTeam(owner=person)
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(person):
-                bug.subscribe(team, person)
-                harness = LaunchpadFormHarness(
-                    bug.default_bugtask, BugSubscriptionSubscribeSelfView)
-                subscription_field = (
-                    harness.view.form_fields['subscription'].field)
-                # The update-subscription option won't appear.
-                self.assertNotIn(
-                    'update-subscription',
-                    subscription_field.vocabulary.by_token)
+        with person_logged_in(person):
+            bug.subscribe(team, person)
+            harness = LaunchpadFormHarness(
+                bug.default_bugtask, BugSubscriptionSubscribeSelfView)
+            subscription_field = (
+                harness.view.form_fields['subscription'].field)
+            # The update-subscription option won't appear.
+            self.assertNotIn(
+                'update-subscription',
+                subscription_field.vocabulary.by_token)
 
     def test_bug_673288(self):
         # If the user is not directly subscribed, but is subscribed via
@@ -223,20 +214,19 @@
         duplicate = self.factory.makeBug()
         person = self.factory.makePerson()
         team = self.factory.makeTeam(owner=person)
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(person):
-                duplicate.markAsDuplicate(bug)
-                duplicate.subscribe(person, person)
-                bug.subscribe(team, person)
+        with person_logged_in(person):
+            duplicate.markAsDuplicate(bug)
+            duplicate.subscribe(person, person)
+            bug.subscribe(team, person)
 
-                harness = LaunchpadFormHarness(
-                    bug.default_bugtask, BugSubscriptionSubscribeSelfView)
-                subscription_field = (
-                    harness.view.form_fields['subscription'].field)
-                # The update-subscription option won't appear.
-                self.assertNotIn(
-                    'update-subscription',
-                    subscription_field.vocabulary.by_token)
+            harness = LaunchpadFormHarness(
+                bug.default_bugtask, BugSubscriptionSubscribeSelfView)
+            subscription_field = (
+                harness.view.form_fields['subscription'].field)
+            # The update-subscription option won't appear.
+            self.assertNotIn(
+                'update-subscription',
+                subscription_field.vocabulary.by_token)
 
     def test_bug_notification_level_field_hidden_for_dupe_subs(self):
         # If the user is subscribed to the bug via a duplicate, the
@@ -244,14 +234,13 @@
         bug = self.factory.makeBug()
         duplicate = self.factory.makeBug()
         person = self.factory.makePerson()
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(person):
-                duplicate.markAsDuplicate(bug)
-                duplicate.subscribe(person, person)
-                harness = LaunchpadFormHarness(
-                    bug.default_bugtask, BugSubscriptionSubscribeSelfView)
-                self.assertFalse(
-                    harness.view.widgets['bug_notification_level'].visible)
+        with person_logged_in(person):
+            duplicate.markAsDuplicate(bug)
+            duplicate.subscribe(person, person)
+            harness = LaunchpadFormHarness(
+                bug.default_bugtask, BugSubscriptionSubscribeSelfView)
+            self.assertFalse(
+                harness.view.widgets['bug_notification_level'].visible)
 
     def test_bug_721400(self):
         # If a subscription exists with a BugNotificationLevel of
@@ -265,18 +254,17 @@
             bug.subscribe(
                 person, person, level=BugNotificationLevel.NOTHING)
 
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(person):
-                subscribe_view = create_initialized_view(
-                    bug.default_bugtask, name='+subscribe')
-                self.assertEqual(0, len(subscribe_view.errors))
-                bug_notification_level_widget = (
-                    subscribe_view.widgets['bug_notification_level'])
-                default_notification_level_value = (
-                    bug_notification_level_widget._getDefault())
-                self.assertEqual(
-                    BugNotificationLevel.COMMENTS,
-                    default_notification_level_value)
+        with person_logged_in(person):
+            subscribe_view = create_initialized_view(
+                bug.default_bugtask, name='+subscribe')
+            self.assertEqual(0, len(subscribe_view.errors))
+            bug_notification_level_widget = (
+                subscribe_view.widgets['bug_notification_level'])
+            default_notification_level_value = (
+                bug_notification_level_widget._getDefault())
+            self.assertEqual(
+                BugNotificationLevel.COMMENTS,
+                default_notification_level_value)
 
     def test_muted_subs_have_unmute_option(self):
         # If a user has a muted subscription, the
@@ -285,17 +273,16 @@
         with person_logged_in(self.person):
             self.bug.mute(self.person, self.person)
 
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(self.person):
-                subscribe_view = create_initialized_view(
-                    self.bug.default_bugtask, name='+subscribe')
-                subscription_widget = (
-                    subscribe_view.widgets['subscription'])
-                # The Unmute option is actually treated the same way as
-                # the unsubscribe option.
-                self.assertEqual(
-                    "unmute bug mail from this bug, or",
-                    subscription_widget.vocabulary.getTerm(self.person).title)
+        with person_logged_in(self.person):
+            subscribe_view = create_initialized_view(
+                self.bug.default_bugtask, name='+subscribe')
+            subscription_widget = (
+                subscribe_view.widgets['subscription'])
+            # The Unmute option is actually treated the same way as
+            # the unsubscribe option.
+            self.assertEqual(
+                "unmute bug mail from this bug, or",
+                subscription_widget.vocabulary.getTerm(self.person).title)
 
     def test_muted_subs_have_unmute_and_update_option(self):
         # If a user has a muted subscription, the
@@ -305,17 +292,16 @@
         with person_logged_in(self.person):
             self.bug.mute(self.person, self.person)
 
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(self.person):
-                subscribe_view = create_initialized_view(
-                    self.bug.default_bugtask, name='+subscribe')
-                subscription_widget = (
-                    subscribe_view.widgets['subscription'])
-                update_term = subscription_widget.vocabulary.getTermByToken(
-                    'update-subscription')
-                self.assertEqual(
-                    "unmute bug mail from this bug and subscribe me to it.",
-                    update_term.title)
+        with person_logged_in(self.person):
+            subscribe_view = create_initialized_view(
+                self.bug.default_bugtask, name='+subscribe')
+            subscription_widget = (
+                subscribe_view.widgets['subscription'])
+            update_term = subscription_widget.vocabulary.getTermByToken(
+                'update-subscription')
+            self.assertEqual(
+                "unmute bug mail from this bug and subscribe me to it.",
+                update_term.title)
 
     def test_unmute_unmutes(self):
         # Using the "Unmute bug mail" option when the user has a muted
@@ -323,21 +309,20 @@
         with person_logged_in(self.person):
             self.bug.mute(self.person, self.person)
 
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(self.person):
-                level = BugNotificationLevel.METADATA
-                form_data = {
-                    'field.subscription': self.person.name,
-                    # Although this isn't used we must pass it for the
-                    # sake of form validation.
-                    'field.bug_notification_level': level.title,
-                    'field.actions.continue': 'Continue',
-                    }
-                create_initialized_view(
-                    self.bug.default_bugtask, form=form_data,
-                    name='+subscribe')
-                self.assertFalse(self.bug.isMuted(self.person))
-                self.assertFalse(self.bug.isSubscribed(self.person))
+        with person_logged_in(self.person):
+            level = BugNotificationLevel.METADATA
+            form_data = {
+                'field.subscription': self.person.name,
+                # Although this isn't used we must pass it for the
+                # sake of form validation.
+                'field.bug_notification_level': level.title,
+                'field.actions.continue': 'Continue',
+                }
+            create_initialized_view(
+                self.bug.default_bugtask, form=form_data,
+                name='+subscribe')
+            self.assertFalse(self.bug.isMuted(self.person))
+            self.assertFalse(self.bug.isSubscribed(self.person))
 
     def test_update_when_muted_updates(self):
         # Using the "Unmute and subscribe me" option when the user has a
@@ -346,39 +331,36 @@
         with person_logged_in(self.person):
             muted_subscription = self.bug.mute(self.person, self.person)
 
-        with FeatureFixture({self.feature_flag: ON}):
-            with person_logged_in(self.person):
-                level = BugNotificationLevel.COMMENTS
-                form_data = {
-                    'field.subscription': 'update-subscription',
-                    'field.bug_notification_level': level.title,
-                    'field.actions.continue': 'Continue',
-                    }
-                create_initialized_view(
-                    self.bug.default_bugtask, form=form_data,
-                    name='+subscribe')
-                self.assertFalse(self.bug.isMuted(self.person))
-                self.assertTrue(self.bug.isSubscribed(self.person))
-                self.assertEqual(
-                    level, muted_subscription.bug_notification_level)
+        with person_logged_in(self.person):
+            level = BugNotificationLevel.COMMENTS
+            form_data = {
+                'field.subscription': 'update-subscription',
+                'field.bug_notification_level': level.title,
+                'field.actions.continue': 'Continue',
+                }
+            create_initialized_view(
+                self.bug.default_bugtask, form=form_data,
+                name='+subscribe')
+            self.assertFalse(self.bug.isMuted(self.person))
+            self.assertTrue(self.bug.isSubscribed(self.person))
+            self.assertEqual(
+                level, muted_subscription.bug_notification_level)
 
     def test_bug_notification_level_field_has_widget_class(self):
         # The bug_notification_level widget has a widget_class property
         # that can be used to manipulate it with JavaScript.
         with person_logged_in(self.person):
-            with FeatureFixture({self.feature_flag: ON}):
-                subscribe_view = create_initialized_view(
-                    self.bug.default_bugtask, name='+subscribe')
-            widget_class = (
-                subscribe_view.widgets['bug_notification_level'].widget_class)
-            self.assertEqual(
-                'bug-notification-level-field', widget_class)
+            subscribe_view = create_initialized_view(
+                self.bug.default_bugtask, name='+subscribe')
+        widget_class = (
+            subscribe_view.widgets['bug_notification_level'].widget_class)
+        self.assertEqual(
+            'bug-notification-level-field', widget_class)
 
 
 class BugSubscriptionAdvancedFeaturesPortletTestCase(TestCaseWithFactory):
 
     layer = LaunchpadFunctionalLayer
-    feature_flag = 'malone.advanced-subscriptions.enabled'
 
     def setUp(self):
         super(BugSubscriptionAdvancedFeaturesPortletTestCase, self).setUp()
@@ -389,24 +371,11 @@
         with person_logged_in(self.person):
             self.target.addBugSubscription(subscriber, subscriber)
 
-    def get_contents(self, flag):
+    def get_contents(self):
         with person_logged_in(self.person):
-            with FeatureFixture({self.feature_flag: flag}):
-                bug_view = create_initialized_view(
-                    self.bug, name="+bug-portlet-subscribers-content")
-                return bug_view.render()
-
-    def test_also_notified_suppressed(self):
-        # If the advanced-subscription.enabled feature flag is on then the
-        # "Also notified" portion of the portlet is suppressed.
-        contents = self.get_contents(ON)
-        self.assertFalse('Also notified' in contents)
-
-    def test_also_notified_not_suppressed(self):
-        # If the advanced-subscription.enabled feature flag is off then the
-        # "Also notified" portion of the portlet is shown.
-        contents = self.get_contents(OFF)
-        self.assertTrue('Also notified' in contents)
+            bug_view = create_initialized_view(
+                self.bug, name="+bug-portlet-subscribers-content")
+            return bug_view.render()
 
 
 class BugPortletSubcribersIdsTests(TestCaseWithFactory):

=== modified file 'lib/lp/bugs/browser/tests/test_bugsubscriptionfilter.py'
--- lib/lp/bugs/browser/tests/test_bugsubscriptionfilter.py	2011-03-30 15:20:46 +0000
+++ lib/lp/bugs/browser/tests/test_bugsubscriptionfilter.py	2011-05-24 18:03:27 +0000
@@ -31,7 +31,6 @@
     )
 from lp.testing import (
     feature_flags,
-    set_feature_flag,
     anonymous_logged_in,
     login_person,
     normalize_whitespace,
@@ -497,15 +496,12 @@
     def setUp(self):
         super(TestBugSubscriptionFilterAdvancedFeatures, self).setUp()
         self.setUpTarget()
-        with feature_flags():
-            set_feature_flag(u'malone.advanced-subscriptions.enabled', u'on')
 
     def setUpTarget(self):
         self.target = self.factory.makeProduct()
 
     def test_filter_uses_bug_notification_level(self):
-        # When advanced features are turned on for subscriptions a user
-        # can specify a bug_notification_level on the +filter form.
+        # A user can specify a bug_notification_level on the +filter form.
         with feature_flags():
             # We don't display BugNotificationLevel.NOTHING as an option.
             displayed_levels = [
@@ -526,7 +522,7 @@
                         'field.bug_notification_level': level.title,
                         "field.actions.create": "Create",
                         }
-                    view = create_initialized_view(
+                    create_initialized_view(
                         subscription, name="+new-filter", form=form)
 
                 filters = subscription.bug_filters
@@ -559,21 +555,6 @@
                     subscription, name="+new-filter", form=form)
                 self.assertTrue(view.errors)
 
-    def test_extra_features_hidden_without_feature_flag(self):
-        # If the malone.advanced-subscriptions.enabled flag is turned
-        # off, the bug_notification_level field doesn't appear on the
-        # form.  This is actually not important for the filter, but when
-        # this test fails because we no longer rely on a feature flag, it
-        # can be a reminder to clean up the rest of this test to get
-        # rid of the feature flag code.
-        person = self.factory.makePerson()
-        with person_logged_in(person):
-            subscription = self.target.addBugSubscription(person, person)
-            view = create_initialized_view(subscription, name="+new-filter")
-            form_fields = view.form_fields
-            self.assertIs(
-                None, form_fields.get('bug_notification_level'))
-
 
 class TestBugSubscriptionFilterCreateView(TestCaseWithFactory):
 

=== modified file 'lib/lp/bugs/browser/tests/test_bugtask.py'
--- lib/lp/bugs/browser/tests/test_bugtask.py	2011-05-17 00:43:05 +0000
+++ lib/lp/bugs/browser/tests/test_bugtask.py	2011-05-24 18:03:27 +0000
@@ -91,7 +91,7 @@
         self.getUserBrowser(url, person_no_teams)
         # This may seem large: it is; there is easily another 30% fat in
         # there.
-        self.assertThat(recorder, HasQueryCount(LessThan(69)))
+        self.assertThat(recorder, HasQueryCount(LessThan(74)))
         count_with_no_teams = recorder.count
         # count with many teams
         self.invalidate_caches(task)
@@ -107,7 +107,7 @@
     def test_rendered_query_counts_constant_with_attachments(self):
         with celebrity_logged_in('admin'):
             browses_under_limit = BrowsesWithQueryLimit(
-                73, self.factory.makePerson())
+                78, self.factory.makePerson())
 
             # First test with a single attachment.
             task = self.factory.makeBugTask()
@@ -647,13 +647,13 @@
         ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
         dsp_1 = self.factory.makeDistributionSourcePackage(
             distribution=ubuntu, sourcepackagename='mouse')
-        ignore = self.factory.makeSourcePackagePublishingHistory(
+        self.factory.makeSourcePackagePublishingHistory(
             distroseries=ubuntu.currentseries,
             sourcepackagename=dsp_1.sourcepackagename)
         bug_task_1 = self.factory.makeBugTask(target=dsp_1)
         dsp_2 = self.factory.makeDistributionSourcePackage(
             distribution=ubuntu, sourcepackagename='rabbit')
-        ignore = self.factory.makeSourcePackagePublishingHistory(
+        self.factory.makeSourcePackagePublishingHistory(
             distroseries=ubuntu.currentseries,
             sourcepackagename=dsp_2.sourcepackagename)
         bug_task_2 = self.factory.makeBugTask(

=== modified file 'lib/lp/bugs/stories/bug-privacy/05-set-bug-private-as-admin.txt'
--- lib/lp/bugs/stories/bug-privacy/05-set-bug-private-as-admin.txt	2010-12-24 10:37:15 +0000
+++ lib/lp/bugs/stories/bug-privacy/05-set-bug-private-as-admin.txt	2011-05-24 18:03:27 +0000
@@ -20,41 +20,44 @@
 
     >>> print_subscribers_from_duplicates(browser.contents)
     From duplicates:
+
+At the moment the "Also notified" area is blank.  That will change when bug
+772754 is fixed.
+
     >>> print_also_notified(browser.contents)
     Also notified:
-    Sample Person
-    Ubuntu Team
 
 Foo Bar is not Cc'd on this bug, but is able to set the bug private
 anyway, because he is an admin.
 
-  >>> browser.open(
-  ...     "http://bugs.launchpad.dev/debian/+source/mozilla-firefox/";
-  ...     "+bug/2/+secrecy")
-  >>> browser.getControl("This bug report should be private").selected = True
-  >>> browser.getControl("Change").click()
-  >>> print browser.url
-  http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/2
+    >>> browser.open(
+    ...     "http://bugs.launchpad.dev/debian/+source/mozilla-firefox/";
+    ...     "+bug/2/+secrecy")
+    >>> browser.getControl(
+    ...     "This bug report should be private").selected = True
+    >>> browser.getControl("Change").click()
+    >>> print browser.url
+    http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/2
 
 
 All the implicit subscribers have been made explicit.
 
-  >>> browser.open(
-  ...     "http://launchpad.dev/bugs/2/+bug-portlet-subscribers-content";)
-  >>> print_direct_subscribers(browser.contents)
-  Ubuntu Team (Subscribed by Foo Bar) (Unsubscribe Ubuntu Team)
-  Sample Person (Subscribed by Foo Bar)
-  Steve Alexander (Subscribed by Launchpad Janitor)
+    >>> browser.open(
+    ...     "http://launchpad.dev/bugs/2/+bug-portlet-subscribers-content";)
+    >>> print_direct_subscribers(browser.contents)
+    Ubuntu Team (Subscribed by Foo Bar) (Unsubscribe Ubuntu Team)
+    Sample Person (Subscribed by Foo Bar)
+    Steve Alexander (Subscribed by Launchpad Janitor)
 
-  >>> print_subscribers_from_duplicates(browser.contents)
-  From duplicates:
-  >>> print_also_notified(browser.contents)
-  Also notified:
+    >>> print_subscribers_from_duplicates(browser.contents)
+    From duplicates:
+    >>> print_also_notified(browser.contents)
+    Also notified:
 
 When we go back to the secrecy form, the previously set value is pre-selected.
 
-  >>> browser.open(
-  ...     "http://bugs.launchpad.dev/debian/+source/mozilla-firefox/";
-  ...     "+bug/2/+secrecy")
-  >>> browser.getControl("This bug report should be private").selected
-  True
+    >>> browser.open(
+    ...     "http://bugs.launchpad.dev/debian/+source/mozilla-firefox/";
+    ...     "+bug/2/+secrecy")
+    >>> browser.getControl("This bug report should be private").selected
+    True

=== modified file 'lib/lp/bugs/stories/bug-privacy/40-unsubscribe-from-private-bug.txt'
--- lib/lp/bugs/stories/bug-privacy/40-unsubscribe-from-private-bug.txt	2010-10-18 22:24:59 +0000
+++ lib/lp/bugs/stories/bug-privacy/40-unsubscribe-from-private-bug.txt	2011-05-24 18:03:27 +0000
@@ -5,7 +5,9 @@
     >>> from canonical.launchpad.webapp.interfaces import ILaunchBag
     >>> from lp.bugs.interfaces.bugtask import BugTaskSearchParams
     >>> from lp.registry.interfaces.distribution import IDistributionSet
-    >>> from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
+    >>> from lp.registry.interfaces.sourcepackagename import (
+    ...     ISourcePackageNameSet,
+    ...     )
     >>> from canonical.launchpad.ftests import login, logout
 
     >>> login("foo.bar@xxxxxxxxxxxxx")
@@ -32,34 +34,3 @@
     >>> browser.getControl("Subscribe user").click()
     >>> browser.url
     'http://bugs.launchpad.dev/ubuntu/+source/evolution/+bug/...'
-
-If Sample Person decides to unsubscribe from the bug, he'll be
-redirected to the bug listing page, because he will no longer have
-permission to see the private bug.
-
-    >>> browser = setupBrowser(auth="Basic test@xxxxxxxxxxxxx:test")
-    >>> unsubscribe_url = (
-    ...     "http://launchpad.dev/ubuntu/+source/evolution/+bug/%s";
-    ...     "/+subscribe" % latest_evo_bug)
-    >>> browser.open(unsubscribe_url)
-    >>> browser.getControl("Continue").click()
-
-    >>> browser.url
-    'http://launchpad.dev/ubuntu/+source/evolution/+bugs'
-
-    >>> "You have been unsubscribed" in browser.contents
-    True
-
-But if Foo Bar decides to unsubscribe from the private bug, he won't be
-redirected to the bug listing because he still has permission to view
-the private bug, because admins can see all bugs.
-
-    >>> browser = setupBrowser(auth="Basic foo.bar@xxxxxxxxxxxxx:test")
-    >>> browser.open(unsubscribe_url)
-    >>> browser.getControl("Continue").click()
-
-    >>> browser.url
-    'http://bugs.launchpad.dev/ubuntu/+source/evolution/+bug/...'
-    >>> "You have been unsubscribed" in browser.contents
-    True
-

=== modified file 'lib/lp/bugs/stories/bugs/bug-add-subscriber.txt'
--- lib/lp/bugs/stories/bugs/bug-add-subscriber.txt	2010-04-29 17:49:19 +0000
+++ lib/lp/bugs/stories/bugs/bug-add-subscriber.txt	2011-05-24 18:03:27 +0000
@@ -43,15 +43,17 @@
     Steve Alexander (Subscribed by Launchpad Janitor)
     >>> print_subscribers_from_duplicates(user_browser.contents)
     From duplicates:
+
+At the moment the "Also notified" area is blank.  That will change when bug
+772754 is fixed.
+
     >>> print_also_notified(user_browser.contents)
     Also notified:
-    Foo Bar
-    Mark Shuttleworth
-    Ubuntu Team
 
 He subscribes David Allouche to the bug using his Launchpad username.
 
-    >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/1/+addsubscriber')
+    >>> user_browser.open(
+    ...     'http://bugs.launchpad.dev/firefox/+bug/1/+addsubscriber')
     >>> user_browser.getControl("Person").value = 'ddaa'
     >>> user_browser.getControl("Subscribe user").click()
     >>> user_browser.url
@@ -87,7 +89,7 @@
     ...
     X-Launchpad-Message-Rationale: Subscriber
     ...
-    You have been subscribed to a public bug by No Privileges Person (no-priv):
+    You have been subscribed to a public bug by No Privileges Person...
     ...
     http://bugs.launchpad.dev/bugs/...
 

=== modified file 'lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions-advanced-features.txt'
--- lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions-advanced-features.txt	2011-04-15 11:02:24 +0000
+++ lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions-advanced-features.txt	2011-05-24 18:03:27 +0000
@@ -1,19 +1,8 @@
 Advanced personal subscriptions
 -------------------------------
 
-There are advanced features for bug subscriptions. These require a
-feature flag to be turned on for them to be accessible.
-
-    >>> from lp.services.features.model import FeatureFlag, getFeatureStore
-    >>> feature_store = getFeatureStore()
-    >>> ignore = feature_store.add(FeatureFlag(
-    ...     scope=u'default',
-    ...     flag=u'malone.advanced-subscriptions.enabled',
-    ...     value=u'on', priority=1))
-    >>> feature_store.flush()
-
-Now when a user visits the +subscribe page of a bug they are given the
-option to subscribe to the bug at a given BugNotificationLevel.
+When a user visits the +subscribe page of a bug they are given the option to
+subscribe to the bug at a given BugNotificationLevel.
 
     >>> from canonical.launchpad.webapp import canonical_url
     >>> from lp.testing.sampledata import USER_EMAIL
@@ -30,8 +19,8 @@
     Details
     Lifecycle
 
-The user can now subscribe to the bug at any of the given notification
-levels. In this case, they want to subscribe to just metadata updates:
+The user can subscribe to the bug at any of the given notification levels. In
+this case, they want to subscribe to just metadata updates:
 
     >>> bug_notification_level_control.getControl(
     ...     'any change is made to this bug, other than a new comment '

=== modified file 'lib/lp/bugs/stories/bugs/xx-distribution-bugs-page.txt'
--- lib/lp/bugs/stories/bugs/xx-distribution-bugs-page.txt	2009-10-02 15:16:03 +0000
+++ lib/lp/bugs/stories/bugs/xx-distribution-bugs-page.txt	2011-05-24 18:03:27 +0000
@@ -1,4 +1,5 @@
-== The Distribution Bugs Page ==
+The Distribution Bugs Page
+--------------------------
 
 The front page for a distribution on the bugs domain presents some basic
 information the bugs in it. It doesn't display the list of bugs.
@@ -17,7 +18,8 @@
     >>> find_tag_by_id(anon_browser.contents, 'buglisting') is not None
     True
 
-It also has a link to subscribe to bug mail.
+It also has a link to subscribe to bug mail (which is implemented in
+JavaScript so it doesn't actually go anywhere).
 
     >>> user_browser.open('http://bugs.launchpad.dev/ubuntu')
     >>> user_browser.getLink('Subscribe to bug mail').click()
@@ -25,7 +27,8 @@
     http://bugs.launchpad.dev/ubuntu/+subscribe
 
 
-== Bugs Fixed Elsewhere ==
+Bugs Fixed Elsewhere
+--------------------
 
 The Bugs frontpage for a distribution includes the number of bugs that are
 fixed in some other context.
@@ -45,7 +48,8 @@
     ...
 
 
-== CVE Bugs ==
+CVE Bugs
+--------
 
 It also displays the number of open bugs associated with a CVE.
 
@@ -67,7 +71,8 @@
       Medium New
 
 
-== Expirable Bugs ==
+Expirable Bugs
+--------------
 
 The bugs page displays the number of Incomplete, unattended bugs that
 can expire when the project has enabled bug expiration.

=== modified file 'lib/lp/bugs/stories/bugs/xx-product-bugs-page.txt'
--- lib/lp/bugs/stories/bugs/xx-product-bugs-page.txt	2010-08-05 07:07:06 +0000
+++ lib/lp/bugs/stories/bugs/xx-product-bugs-page.txt	2011-05-24 18:03:27 +0000
@@ -1,4 +1,5 @@
-== The Product Bugs Page ==
+The Product Bugs Page
+---------------------
 
 The front page for a product on the bugs domain presents some basic
 information the bugs in it. It doesn't display the list of bugs.
@@ -17,14 +18,16 @@
     >>> find_tag_by_id(anon_browser.contents, 'buglisting') is not None
     True
 
-It also has a link to subscribe to bug mail.
+It also has a link to subscribe to bug mail (which is implemented in
+JavaScript so it doesn't actually go anywhere).
 
     >>> user_browser.open('http://bugs.launchpad.dev/firefox')
     >>> user_browser.getLink('Subscribe to bug mail').click()
     >>> user_browser.url
     'http://bugs.launchpad.dev/firefox/+subscribe'
 
-== CVE Bugs ==
+CVE Bugs
+--------
 
 It also displays the number of open bugs associated with a CVE.
 
@@ -45,7 +48,8 @@
       Low New
 
 
-== Bugs Fixed Elsewhere ==
+Bugs Fixed Elsewhere
+--------------------
 
 The Bugs front page includes the number of bugs that are fixed in some
 other context. For example, users can see all bugs raised against
@@ -66,7 +70,8 @@
     ...
 
 
-== Expirable Bugs ==
+Expirable Bugs
+--------------
 
 The bugs page displays the number of Incomplete, unattended bugs that
 can expire when the project has enabled bug expiration. Jokosher
@@ -101,7 +106,8 @@
     LinkNotFoundError
 
 
-== Tags and Filters ==
+Tags and Filters
+----------------
 
 There's also portlets for easy searching by tags and filters. Its content is
 loaded using Javascript in a separate request.
@@ -129,7 +135,8 @@
       Critical New
 
 
-== Hot Bugs ==
+Hot Bugs
+--------
 
 A listing of the 10 'hottest' bugs is displayed to allow a quick
 overview of the project.

=== modified file 'lib/lp/bugs/stories/structural-subscriptions/xx-advanced-features.txt'
--- lib/lp/bugs/stories/structural-subscriptions/xx-advanced-features.txt	2011-04-07 09:38:31 +0000
+++ lib/lp/bugs/stories/structural-subscriptions/xx-advanced-features.txt	2011-05-24 18:03:27 +0000
@@ -1,18 +1,7 @@
 Advanced structural subscriptions
 ---------------------------------
 
-There are advanced features for structural bug subscriptions.
-These require a feature flag to be turned on for them to be accessible.
-
-    >>> from lp.services.features.model import FeatureFlag, getFeatureStore
-    >>> feature_store = getFeatureStore()
-    >>> ignore = feature_store.add(FeatureFlag(
-    ...     scope=u'default',
-    ...     flag=u'malone.advanced-structural-subscriptions.enabled',
-    ...     value=u'on', priority=1))
-    >>> feature_store.flush()
-
-Now when a user visits the +subscriptions page of a product they are given the
+When a user visits the +subscriptions page of a product they are given the
 option to subscribe to add a new subscription
 
     >>> from canonical.launchpad.webapp import canonical_url

=== modified file 'lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt'
--- lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt	2011-03-23 16:28:51 +0000
+++ lib/lp/bugs/stories/structural-subscriptions/xx-bug-subscriptions.txt	2011-05-24 18:03:27 +0000
@@ -195,23 +195,6 @@
     LookupError: label '\xa0Foo Bar'
 
 
-Bug #462742
-===========
-
-Loading pages with the subscription menu works fine for anonymous users, even
-when there are existing subscriptions.
-
-    >>> browser.open('http://launchpad.dev/bzr')
-    >>> browser.getLink('Subscribe to bug mail').click()
-    >>> browser.getControl(
-    ...     'I want to receive these '
-    ...     'notifications by e-mail.').selected = True
-    >>> browser.getControl('Save these changes').click()
-
-    >>> anon_browser.open('http://launchpad.dev/bzr')
-    >>> print anon_browser.title
-    Bazaar Version Control System in Launchpad
-
 Distribution with a bug supervisor
 ==================================
 

=== modified file 'lib/lp/bugs/stories/xx-bugs-statistics-portlet.txt'
--- lib/lp/bugs/stories/xx-bugs-statistics-portlet.txt	2011-05-24 18:03:26 +0000
+++ lib/lp/bugs/stories/xx-bugs-statistics-portlet.txt	2011-05-24 18:03:27 +0000
@@ -28,8 +28,7 @@
     Bugs with patches
     Bugs fixed elsewhere
     Open CVE bugs - CVE reports
-    Subscribe to bug mail
-      --> http://bugs.launchpad.dev/debian/+subscribe
+
 
     >>> print_bugfilters_portlet_filled(anon_browser, path)
     1 New bug
@@ -57,8 +56,7 @@
     Bugs with patches
     Bugs fixed elsewhere
     Open CVE bugs - CVE reports
-    Subscribe to bug mail
-      --> http://bugs.launchpad.dev/debian/+subscribe
+
 
     >>> print_bugfilters_portlet_filled(user_browser, path)
     1 New bug
@@ -97,8 +95,6 @@
     Bugs with patches
     Bugs fixed elsewhere
     Open CVE bugs - CVE reports
-    Subscribe to bug mail
-      --> http://bugs.launchpad.dev/debian/woody/+subscribe
     Review nominations
       --> http://bugs.launchpad.dev/debian/woody/+nominations
 
@@ -128,8 +124,6 @@
     Bugs with patches
     Bugs fixed elsewhere
     Open CVE bugs - CVE reports
-    Subscribe to bug mail
-      --> http://bugs.launchpad.dev/debian/woody/+subscribe
     Review nominations
       --> http://bugs.launchpad.dev/debian/woody/+nominations
 
@@ -169,8 +163,6 @@
     Bugs with patches
     Bugs fixed elsewhere
     Open CVE bugs
-    Subscribe to bug mail
-      --> http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+subscribe
 
     >>> print_bugfilters_portlet_filled(anon_browser, path)
     1 New bug
@@ -198,8 +190,6 @@
     Bugs with patches
     Bugs fixed elsewhere
     Open CVE bugs
-    Subscribe to bug mail
-      --> http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+subscribe
 
     >>> print_bugfilters_portlet_filled(user_browser, path)
     1 New bug
@@ -308,8 +298,6 @@
     Bugs with patches
     Bugs fixed elsewhere
     Open CVE bugs
-    Subscribe to bug mail
-      --> http://bugs.launchpad.dev/mozilla/+subscribe
 
     >>> print_bugfilters_portlet_filled(anon_browser, path)
     4 New bugs
@@ -339,6 +327,9 @@
     Open CVE bugs
     Subscribe to bug mail
       --> http://bugs.launchpad.dev/mozilla/+subscribe
+    Edit bug mail
+      --> http://bugs.launchpad.dev/mozilla/+subscriptions
+
 
     >>> print_bugfilters_portlet_filled(user_browser, path)
     4 New bugs
@@ -379,8 +370,6 @@
     Bugs with patches
     Bugs fixed elsewhere
     Open CVE bugs - CVE reports
-    Subscribe to bug mail
-      --> http://bugs.launchpad.dev/firefox/+subscribe
 
     >>> print_bugfilters_portlet_filled(anon_browser, path)
     3 New bugs
@@ -410,6 +399,9 @@
     Open CVE bugs - CVE reports
     Subscribe to bug mail
       --> http://bugs.launchpad.dev/firefox/+subscribe
+    Edit bug mail
+      --> http://bugs.launchpad.dev/firefox/+subscriptions
+
 
     >>> print_bugfilters_portlet_filled(user_browser, path)
     3 New bugs

=== modified file 'lib/lp/bugs/templates/bug-portlet-subscribers-content.pt'
--- lib/lp/bugs/templates/bug-portlet-subscribers-content.pt	2011-05-18 18:34:19 +0000
+++ lib/lp/bugs/templates/bug-portlet-subscribers-content.pt	2011-05-24 18:03:27 +0000
@@ -57,18 +57,4 @@
       <img src="/@@/spinner" />
     </div>
   </div>
-  <tal:also_notified condition="not: request/features/malone.advanced-subscriptions.enabled">
-    <div
-      tal:define="also_notified_subscribers bug/getAlsoNotifiedSubscribers"
-      tal:condition="also_notified_subscribers"
-      id="subscribers-indirect"
-      class="Section"
-    >
-      <h2>Also notified</h2>
-      <div
-        tal:repeat="subscriber also_notified_subscribers"
-        tal:content="structure subscriber/fmt:link"
-      />
-    </div>
-  </tal:also_notified>
 </div>

=== modified file 'lib/lp/bugs/templates/bug-portlet-subscribers.pt'
--- lib/lp/bugs/templates/bug-portlet-subscribers.pt	2011-05-10 11:30:46 +0000
+++ lib/lp/bugs/templates/bug-portlet-subscribers.pt	2011-05-24 18:03:27 +0000
@@ -13,19 +13,15 @@
       tal:content="structure context_menu/subscription/render" />
     <div id="sub-unsub-spinner">Subscribing...</div>
     <div tal:content="structure context_menu/addsubscriber/render" />
-    <div tal:condition="request/features/malone.advanced-structural-subscriptions.enabled"
-        tal:content="structure context_menu/editsubscriptions/render" />
-    <tal:show-mute condition="
-        request/features/malone.advanced-subscriptions.enabled">
-      <div tal:attributes="class view/current_user_mute_class"
-           id="mute-link-container">
-        <span tal:replace="structure context_menu/mute_subscription/render"/>
-        <a target="help" class="sprite maybe mute-help"
-            href="/+help/subscription-mute.html"
-          >&nbsp;<span class="invisible-link">Mute help</span></a>
-        <div style="float: left" id="mute-unmute-spinner">Unmuting...</div>
-      </div>
-    </tal:show-mute>
+    <div tal:content="structure context_menu/editsubscriptions/render" />
+    <div tal:attributes="class view/current_user_mute_class"
+          id="mute-link-container">
+      <span tal:replace="structure context_menu/mute_subscription/render"/>
+      <a target="help" class="sprite maybe mute-help"
+          href="/+help/subscription-mute.html"
+        >&nbsp;<span class="invisible-link">Mute help</span></a>
+      <div style="float: left" id="mute-unmute-spinner">Unmuting...</div>
+    </div>
   </div>
   <a id="subscribers-ids-link"
      tal:define="bug context/bug|context"

=== modified file 'lib/lp/bugs/templates/bug-subscription-list.pt'
--- lib/lp/bugs/templates/bug-subscription-list.pt	2011-04-14 15:16:22 +0000
+++ lib/lp/bugs/templates/bug-subscription-list.pt	2011-05-24 18:03:27 +0000
@@ -12,9 +12,7 @@
 
 <head>
   <tal:head-epilogue metal:fill-slot="head_epilogue">
-    <script type="text/javascript"
-        tal:condition="
-          request/features/malone.advanced-structural-subscriptions.enabled">
+    <script type="text/javascript">
       LPS.use('lp.registry.structural_subscription', 'lp.bugs.subscription',
               function(Y) {
           var ss_module = Y.lp.registry.structural_subscription;

=== modified file 'lib/lp/bugs/templates/buglisting-default.pt'
--- lib/lp/bugs/templates/buglisting-default.pt	2011-03-29 22:34:04 +0000
+++ lib/lp/bugs/templates/buglisting-default.pt	2011-05-24 18:03:27 +0000
@@ -10,9 +10,7 @@
 <metal:block fill-slot="head_epilogue">
   <meta condition="not: view/should_show_bug_information"
         name="robots" content="noindex,nofollow" />
-  <script type="text/javascript"
-      tal:condition="
-        request/features/malone.advanced-structural-subscriptions.enabled">
+  <script type="text/javascript">
     LPS.use('lp.registry.structural_subscription', function(Y) {
         var module = Y.lp.registry.structural_subscription;
         Y.on('domready', function() {

=== modified file 'lib/lp/bugs/templates/bugtarget-bugs.pt'
--- lib/lp/bugs/templates/bugtarget-bugs.pt	2011-05-04 20:55:07 +0000
+++ lib/lp/bugs/templates/bugtarget-bugs.pt	2011-05-24 18:03:27 +0000
@@ -14,9 +14,7 @@
           name="robots" content="noindex,nofollow" />
     <tal:uses_launchpad_bugtracker
        condition="view/bug_tracking_usage/enumvalue:LAUNCHPAD">
-      <script type="text/javascript"
-          tal:condition="
-            request/features/malone.advanced-structural-subscriptions.enabled">
+      <script type="text/javascript">
         LPS.use('lp.registry.structural_subscription', function(Y) {
             var module = Y.lp.registry.structural_subscription;
             Y.on('domready', function() {

=== modified file 'lib/lp/bugs/templates/bugtarget-portlet-bugfilters.pt'
--- lib/lp/bugs/templates/bugtarget-portlet-bugfilters.pt	2011-04-04 18:51:17 +0000
+++ lib/lp/bugs/templates/bugtarget-portlet-bugfilters.pt	2011-05-24 18:03:27 +0000
@@ -11,53 +11,33 @@
            tal:content="structure context/@@+bugtarget-portlet-bugfilters-info" />
     <tbody tal:define="menu context/menu:bugs">
 
-      <tal:advanced-structural-subscriptions
-         condition="request/features/malone.advanced-structural-subscriptions.enabled">
-        <tr class="menu-link-subscribe_to_bug_mail invisible-link"
-            tal:define="link menu/subscribe_to_bug_mail|nothing"
-            tal:condition="python: link and link.enabled">
-          <td class="bugs-count" style="padding-top: 3px">
-            <a tal:attributes="href link/url">
-              <img tal:attributes="src link/icon_url" />
-            </a>
-          </td>
-          <td class="bugs-link">
-            <a class="js-action"
-               tal:attributes="href link/url"
-               tal:content="link/escapedtext" />
-          </td>
-        </tr>
-        <tr class="menu-link-edit_bug_mail"
-            tal:define="link menu/edit_bug_mail|nothing"
-            tal:condition="python: link and link.enabled">
-          <td class="bugs-count" style="padding-top: 3px">
-            <a tal:attributes="href link/url">
-              <img tal:attributes="src link/icon_url" />
-            </a>
-          </td>
-          <td class="bugs-link">
-            <a tal:attributes="href link/url"
-               tal:content="link/escapedtext" />
-          </td>
-        </tr>
-      </tal:advanced-structural-subscriptions>
-
-      <tal:not-advanced-structural-subscriptions
-         condition="not: request/features/malone.advanced-structural-subscriptions.enabled">
-        <tr class="menu-link-subscribe"
-            tal:define="subscribe_link menu/subscribe|nothing"
-            tal:condition="python: subscribe_link and subscribe_link.enabled">
-          <td class="bugs-count" style="padding-top: 3px">
-            <a tal:attributes="href subscribe_link/url">
-              <img tal:attributes="src subscribe_link/icon_url" />
-            </a>
-          </td>
-          <td class="bugs-link">
-            <a tal:attributes="href subscribe_link/url"
-               tal:content="subscribe_link/escapedtext" />
-          </td>
-        </tr>
-      </tal:not-advanced-structural-subscriptions>
+      <tr class="menu-link-subscribe_to_bug_mail invisible-link"
+          tal:define="link menu/subscribe_to_bug_mail|nothing"
+          tal:condition="python: link and link.enabled">
+        <td class="bugs-count" style="padding-top: 3px">
+          <a tal:attributes="href link/url">
+            <img tal:attributes="src link/icon_url" />
+          </a>
+        </td>
+        <td class="bugs-link">
+          <a class="js-action"
+              tal:attributes="href link/url"
+              tal:content="link/escapedtext" />
+        </td>
+      </tr>
+      <tr class="menu-link-edit_bug_mail"
+          tal:define="link menu/edit_bug_mail|nothing"
+          tal:condition="python: link and link.enabled">
+        <td class="bugs-count" style="padding-top: 3px">
+          <a tal:attributes="href link/url">
+            <img tal:attributes="src link/icon_url" />
+          </a>
+        </td>
+        <td class="bugs-link">
+          <a tal:attributes="href link/url"
+              tal:content="link/escapedtext" />
+        </td>
+      </tr>
 
       <tr tal:define="review_nominations_link context/menu:bugs/nominations|nothing"
           tal:condition="review_nominations_link"

=== modified file 'lib/lp/bugs/templates/bugtarget-subscription-list.pt'
--- lib/lp/bugs/templates/bugtarget-subscription-list.pt	2011-04-04 18:09:25 +0000
+++ lib/lp/bugs/templates/bugtarget-subscription-list.pt	2011-05-24 18:03:27 +0000
@@ -12,9 +12,7 @@
 
 <head>
   <tal:head-epilogue metal:fill-slot="head_epilogue">
-    <script type="text/javascript"
-        tal:condition="
-          request/features/malone.advanced-structural-subscriptions.enabled">
+    <script type="text/javascript">
       LPS.use('lp.registry.structural_subscription', function(Y) {
           var module = Y.lp.registry.structural_subscription;
           var config = {
@@ -35,8 +33,7 @@
 
     <div id="maincontent">
       <div id="nonportlets" class="readable">
-        <div tal:condition="
-            features/malone.advanced-structural-subscriptions.enabled">
+        <div>
           <a class="sprite add" id="create-new-subscription"
              href="#">Add a subscription</a>
         </div>

=== modified file 'lib/lp/bugs/templates/bugtask-index.pt'
--- lib/lp/bugs/templates/bugtask-index.pt	2011-05-16 15:29:15 +0000
+++ lib/lp/bugs/templates/bugtask-index.pt	2011-05-24 18:03:27 +0000
@@ -10,17 +10,9 @@
       <script type='text/javascript' tal:content="string:var yui_base='${yui}';" />
       <script type='text/javascript' id='available-official-tags-js'
               tal:content="view/available_official_tags_js" />
-      <tal:subscriptions-js
-          define="enabled features/malone.advanced-subscriptions.enabled">
-        <script type="text/javascript" id="use-advanced-subscriptions"
-            tal:condition="enabled">
-          use_advanced_subscriptions = true;
-        </script>
-        <script type="text/javascript" id="use-advanced-subscriptions"
-            tal:condition="not:enabled">
-          use_advanced_subscriptions = false;
-        </script>
-      </tal:subscriptions-js>
+      <script type="text/javascript" id="use-advanced-subscriptions">
+        use_advanced_subscriptions = true;
+      </script>
       <tal:if condition="context/bug/private">
         <script type="text/javascript">
           var bug_private = true;
@@ -31,16 +23,9 @@
           var bug_private = false;
         </script>
       </tal:if>
-      <tal:if condition="request/features/bugs.private_notification.enabled">
-        <script type="text/javascript">
-          var bugs_private_notification_enabled = true;
-        </script>
-      </tal:if>
-      <tal:if condition="not:request/features/bugs.private_notification.enabled">
-        <script type="text/javascript">
-          var bugs_private_notification_enabled = false;
-        </script>
-      </tal:if>
+      <script type="text/javascript">
+        var bugs_private_notification_enabled = true;
+      </script>
       <script type="text/javascript">
         LPS.use('base', 'node', 'oop', 'event', 'lp.bugs.bugtask_index',
                   'lp.bugs.bugtask_index.portlets',
@@ -102,16 +87,14 @@
     </metal:heading>
 
     <div metal:fill-slot="main" tal:define="context_menu context/menu:context">
-      <tal:if condition="request/features/bugs.private_notification.enabled">
-          <div tal:attributes="class string: global-notification ${view/privacy_notice_classes}">
-            <span class="sprite notification-private"></span>
-            This bug is private
-            <a href="#" class="global-notification-close">
-              <span class="sprite notification-close"></span>
-              Hide
-            </a>
-          </div>
-      </tal:if>
+      <div tal:attributes="class string: global-notification ${view/privacy_notice_classes}">
+        <span class="sprite notification-private"></span>
+        This bug is private
+        <a href="#" class="global-notification-close">
+          <span class="sprite notification-close"></span>
+          Hide
+        </a>
+      </div>
       <div id="tags-autocomplete">
        <div id="tags-autocomplete-content"></div>
       </div>

=== modified file 'lib/lp/registry/browser/__init__.py'
--- lib/lp/registry/browser/__init__.py	2011-04-04 18:51:17 +0000
+++ lib/lp/registry/browser/__init__.py	2011-05-24 18:03:27 +0000
@@ -39,7 +39,6 @@
     )
 from lp.registry.interfaces.productseries import IProductSeries
 from lp.registry.interfaces.series import SeriesStatus
-from lp.services.features import getFeatureFlag
 
 
 class StatusCount:
@@ -72,14 +71,9 @@
 
 
 def add_subscribe_link(links):
-    """Based on a feature flag, add the correct link."""
-    use_advanced_features = getFeatureFlag(
-        'malone.advanced-structural-subscriptions.enabled')
-    if use_advanced_features:
-        links.append('subscribe_to_bug_mail')
-        links.append('edit_bug_mail')
-    else:
-        links.append('subscribe')
+    """Add the subscription-related links."""
+    links.append('subscribe_to_bug_mail')
+    links.append('edit_bug_mail')
 
 
 class MilestoneOverlayMixin:

=== modified file 'lib/lp/registry/browser/distribution.py'
--- lib/lp/registry/browser/distribution.py	2011-04-08 11:00:16 +0000
+++ lib/lp/registry/browser/distribution.py	2011-05-24 18:03:27 +0000
@@ -118,7 +118,6 @@
     MirrorSpeed,
     )
 from lp.registry.interfaces.series import SeriesStatus
-from lp.services.features import getFeatureFlag
 from lp.services.geoip.helpers import (
     ipaddress_from_request,
     request_country,
@@ -301,13 +300,7 @@
 
     @cachedproperty
     def links(self):
-        links = ['edit', 'pubconf']
-        use_advanced_features = getFeatureFlag(
-            'malone.advanced-structural-subscriptions.enabled')
-        if use_advanced_features:
-            links.append('subscribe_to_bug_mail')
-            links.append('edit_bug_mail')
-        return links
+        return ['edit', 'pubconf', 'subscribe_to_bug_mail', 'edit_bug_mail']
 
 
 class DistributionOverviewMenu(ApplicationMenu, DistributionLinksMixin):
@@ -1018,9 +1011,9 @@
         if throughput < 1000:
             return str(throughput) + ' Kbps'
         elif throughput < 1000000:
-            return str(throughput/1000) + ' Mbps'
+            return str(throughput / 1000) + ' Mbps'
         else:
-            return str(throughput/1000000) + ' Gbps'
+            return str(throughput / 1000000) + ' Gbps'
 
     @cachedproperty
     def total_throughput(self):

=== modified file 'lib/lp/registry/browser/tests/test_subscription_links.py'
--- lib/lp/registry/browser/tests/test_subscription_links.py	2011-05-10 15:45:06 +0000
+++ lib/lp/registry/browser/tests/test_subscription_links.py	2011-05-24 18:03:27 +0000
@@ -20,7 +20,6 @@
     )
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.model.milestone import ProjectMilestone
-from lp.services.features.testing import FeatureFixture
 from lp.testing import (
     celebrity_logged_in,
     person_logged_in,
@@ -87,62 +86,37 @@
 
 
 class _TestStructSubs(TestCaseWithFactory, _TestResultsMixin):
-    """Test structural subscriptions base class.
-
-    The link to structural subscriptions is controlled by the feature flag
-    'malone.advanced-structural-subscriptions.enabled'.  If it is false, the
-    old link leading to +subscribe is shown.  If it is true then the new
-    JavaScript control is used.
-    """
+    """Test structural subscriptions base class."""
 
     layer = DatabaseFunctionalLayer
-    feature_flag = 'malone.advanced-structural-subscriptions.enabled'
 
     def setUp(self):
         super(_TestStructSubs, self).setUp()
         self.regular_user = self.factory.makePerson()
 
-    def _create_scenario(self, user, flag):
+    def _create_scenario(self, user):
         with person_logged_in(user):
-            with FeatureFixture({self.feature_flag: flag}):
-                view = self.create_view(user)
-                self.contents = view.render()
+            view = self.create_view(user)
+            self.contents = view.render()
 
     def create_view(self, user):
         return create_initialized_view(
             self.target, self.view, principal=user,
             rootsite=self.rootsite, current_request=False)
 
-    def test_subscribe_link_feature_flag_off_owner(self):
-        self._create_scenario(self.target.owner, None)
-        self.assertOldLinkPresent()
-        self.assertNewLinksMissing()
-
     def test_subscribe_link_feature_flag_on_owner(self):
         # Test the new subscription link.
-        self._create_scenario(self.target.owner, 'on')
+        self._create_scenario(self.target.owner)
         self.assertOldLinkMissing()
         self.assertNewLinksPresent()
 
-    def test_subscribe_link_feature_flag_off_user(self):
-        self._create_scenario(self.regular_user, None)
-        self.assertOldLinkPresent()
-        self.assertNewLinksMissing()
-
     def test_subscribe_link_feature_flag_on_user(self):
-        self._create_scenario(self.regular_user, 'on')
+        self._create_scenario(self.regular_user)
         self.assertOldLinkMissing()
         self.assertNewLinksPresent()
 
-    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.assertOldLinkPresent()
-        self.assertNewLinksMissing()
-
     def test_subscribe_link_feature_flag_on_anonymous(self):
-        self._create_scenario(ANONYMOUS, 'on')
+        self._create_scenario(ANONYMOUS)
         # The subscribe link is not shown to anonymous.
         self.assertOldLinkMissing()
         self.assertNewLinksMissing()
@@ -193,7 +167,7 @@
             # IStructuralSubscriptionTargetHelper would attempt to look them
             # up in the database, raising an exception.
             project = self.factory.makeProject()
-            product = self.factory.makeProduct(project=project)
+            self.factory.makeProduct(project=project)
             mixin = StructuralSubscriptionMenuMixin()
             mixin.context = ProjectMilestone(project, '11.04', None, True)
             # Before bug 778689 was fixed, this would raise an exception.
@@ -270,7 +244,6 @@
     """
 
     layer = DatabaseFunctionalLayer
-    feature_flag = 'malone.advanced-structural-subscriptions.enabled'
     rootsite = None
     view = '+index'
 
@@ -283,15 +256,14 @@
 
     def _create_scenario(self, user, flag):
         with person_logged_in(user):
-            with FeatureFixture({self.feature_flag: flag}):
-                logged_in_user = getUtility(ILaunchBag).user
-                no_login = logged_in_user is None
-                browser = self.getViewBrowser(
-                    self.target, view_name=self.view,
-                    rootsite=self.rootsite,
-                    no_login=no_login,
-                    user=logged_in_user)
-                self.contents = browser.contents
+            logged_in_user = getUtility(ILaunchBag).user
+            no_login = logged_in_user is None
+            browser = self.getViewBrowser(
+                self.target, view_name=self.view,
+                rootsite=self.rootsite,
+                no_login=no_login,
+                user=logged_in_user)
+            self.contents = browser.contents
 
     @property
     def old_link(self):
@@ -598,9 +570,9 @@
         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 _DoesNotUseLP(ProductView):
     """Test structural subscriptions on the product view."""
 
@@ -634,8 +606,7 @@
         project = self.factory.makeProject()
         with person_logged_in(self.target.owner):
             self.target.project = project
-        another_product = self.factory.makeProduct(
-            project=project, official_malone=True)
+        self.factory.makeProduct(project=project, official_malone=True)
         self._create_scenario(self.regular_user, 'on')
         self.assertOldLinkMissing()
         self.assertNewLinksMissing()

=== modified file 'lib/lp/registry/templates/distribution-index.pt'
--- lib/lp/registry/templates/distribution-index.pt	2011-04-12 00:06:09 +0000
+++ lib/lp/registry/templates/distribution-index.pt	2011-05-24 18:03:27 +0000
@@ -10,9 +10,7 @@
     <tal:head-epilogue metal:fill-slot="head_epilogue">
       <tal:uses_launchpad_bugtracker
          condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
-        <script type="text/javascript"
-          tal:condition="
-            request/features/malone.advanced-structural-subscriptions.enabled">
+        <script type="text/javascript">
             LPS.use('lp.registry.structural_subscription', function(Y) {
                 var module = Y.lp.registry.structural_subscription;
                 Y.on('domready', function() {

=== modified file 'lib/lp/registry/templates/distributionsourcepackage-index.pt'
--- lib/lp/registry/templates/distributionsourcepackage-index.pt	2011-04-12 00:06:09 +0000
+++ lib/lp/registry/templates/distributionsourcepackage-index.pt	2011-05-24 18:03:27 +0000
@@ -12,9 +12,7 @@
 <metal:block fill-slot="head_epilogue">
   <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">
+    <script type="text/javascript">
       LPS.use('lp.registry.structural_subscription', function(Y) {
           var module = Y.lp.registry.structural_subscription;
           Y.on('domready', function() {

=== modified file 'lib/lp/registry/templates/distroseries-index.pt'
--- lib/lp/registry/templates/distroseries-index.pt	2011-04-15 15:08:20 +0000
+++ lib/lp/registry/templates/distroseries-index.pt	2011-05-24 18:03:27 +0000
@@ -15,9 +15,7 @@
         tal:content="view/register_milestone_script"></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">
+        <script type="text/javascript">
             LPS.use('lp.registry.structural_subscription', function(Y) {
                 var module = Y.lp.registry.structural_subscription;
                 Y.on('domready', function() {

=== modified file 'lib/lp/registry/templates/milestone-index.pt'
--- lib/lp/registry/templates/milestone-index.pt	2011-04-18 14:57:42 +0000
+++ lib/lp/registry/templates/milestone-index.pt	2011-05-24 18:03:27 +0000
@@ -16,9 +16,7 @@
     </tal:is_project>
     <tal:uses_launchpad_bugtracker
        condition="context/target/bug_tracking_usage/enumvalue:LAUNCHPAD">
-      <script type="text/javascript"
-          tal:condition="
-            request/features/malone.advanced-structural-subscriptions.enabled">
+      <script type="text/javascript">
           LPS.use('lp.registry.structural_subscription', function(Y) {
               var module = Y.lp.registry.structural_subscription;
               Y.on('domready', function() {

=== modified file 'lib/lp/registry/templates/product-index.pt'
--- lib/lp/registry/templates/product-index.pt	2011-04-12 00:06:09 +0000
+++ lib/lp/registry/templates/product-index.pt	2011-05-24 18:03:27 +0000
@@ -33,9 +33,7 @@
     </noscript>
     <tal:uses_launchpad_bugtracker
        condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
-      <script type="text/javascript"
-          tal:condition="
-            request/features/malone.advanced-structural-subscriptions.enabled">
+      <script type="text/javascript">
           LPS.use('lp.registry.structural_subscription', function(Y) {
               var module = Y.lp.registry.structural_subscription;
               Y.on('domready', function() {

=== modified file 'lib/lp/registry/templates/productseries-index.pt'
--- lib/lp/registry/templates/productseries-index.pt	2011-04-12 00:06:09 +0000
+++ lib/lp/registry/templates/productseries-index.pt	2011-05-24 18:03:27 +0000
@@ -16,9 +16,7 @@
       tal:content="view/register_milestone_script"></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">
+      <script type="text/javascript">
           LPS.use('lp.registry.structural_subscription', function(Y) {
               var module = Y.lp.registry.structural_subscription;
               Y.on('domready', function() {
@@ -198,10 +196,8 @@
     </div>
 
     <tal:side metal:fill-slot="side"
-      define="overview_menu context/menu:overview;
-              feature_flag request/features/malone.advanced-structural-subscriptions.enabled">
-      <div id="global-actions" class="portlet"
-        tal:condition="overview_menu/subscribe_to_bug_mail/enabled|overview_menu/subscribe/enabled">
+      define="overview_menu context/menu:overview">
+      <div id="global-actions" class="portlet">
         <ul>
           <li tal:condition="overview_menu/edit/enabled">
             <a tal:replace="structure overview_menu/edit/fmt:link" />
@@ -209,21 +205,12 @@
           <li tal:condition="overview_menu/delete/enabled">
             <a tal:replace="structure overview_menu/delete/fmt:link" />
           </li>
-          <tal:advanced-structural-subscriptions
-             condition="feature_flag">
-            <li tal:condition="overview_menu/subscribe_to_bug_mail/enabled|nothing">
-              <a tal:replace="structure overview_menu/subscribe_to_bug_mail/fmt:link" />
-            </li>
-            <li tal:condition="overview_menu/edit_bug_mail/enabled|nothing">
-              <a tal:replace="structure overview_menu/edit_bug_mail/fmt:link" />
-            </li>
-          </tal:advanced-structural-subscriptions>
-          <tal:not-advanced-structural-subscriptions
-             condition="not: feature_flag">
-            <li tal:condition="overview_menu/subscribe/enabled|nothing">
-              <a tal:replace="structure overview_menu/subscribe/fmt:link" />
-            </li>
-          </tal:not-advanced-structural-subscriptions>
+          <li tal:condition="overview_menu/subscribe_to_bug_mail/enabled|nothing">
+            <a tal:replace="structure overview_menu/subscribe_to_bug_mail/fmt:link" />
+          </li>
+          <li tal:condition="overview_menu/edit_bug_mail/enabled|nothing">
+            <a tal:replace="structure overview_menu/edit_bug_mail/fmt:link" />
+          </li>
         </ul>
       </div>
 

=== modified file 'lib/lp/registry/templates/project-index.pt'
--- lib/lp/registry/templates/project-index.pt	2011-04-12 00:06:09 +0000
+++ lib/lp/registry/templates/project-index.pt	2011-05-24 18:03:27 +0000
@@ -11,9 +11,7 @@
     <tal:head-epilogue metal:fill-slot="head_epilogue">
       <tal:uses_launchpad_bugtracker
          condition="context/bug_tracking_usage/enumvalue:LAUNCHPAD">
-        <script type="text/javascript"
-            tal:condition="
-              request/features/malone.advanced-structural-subscriptions.enabled">
+        <script type="text/javascript">
             LPS.use('lp.registry.structural_subscription', function(Y) {
                 var module = Y.lp.registry.structural_subscription;
                 Y.on('domready', function() {

=== modified file 'lib/lp/services/features/flags.py'
--- lib/lp/services/features/flags.py	2011-05-12 06:38:02 +0000
+++ lib/lp/services/features/flags.py	2011-05-24 18:03:27 +0000
@@ -54,14 +54,6 @@
      'boolean',
      'Disable DKIM authentication checks on incoming mail.',
      ''),
-    ('malone.advanced-subscriptions.enabled',
-     'boolean',
-     'Enables advanced bug subscription features.',
-     ''),
-    ('malone.advanced-structural-subscriptions.enabled',
-     'boolean',
-     'Enables advanced structural subscriptions',
-     ''),
     ('malone.disable_targetnamesearch',
      'boolean',
      'If true, disables consultation of target names during bug text search.',