← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stevenk/launchpad/show-visibility-teamadd into lp:launchpad

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/show-visibility-teamadd into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/show-visibility-teamadd/+merge/91389

We'd like to start empowering our users to be able to create private artifacts themselves, if it is justified. This branch is a small first step towards this goal.

To that end, I have created a feature flag, "disclosure.show_visibility_for_team_add.enabled", and if it is enabled, will display the visibility field if and only if the user has a current commercial subscription. I have added all test cases that I could think of, and I have cleaned a little bit of lint, mostly related to imports.
-- 
https://code.launchpad.net/~stevenk/launchpad/show-visibility-teamadd/+merge/91389
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/show-visibility-teamadd into lp:launchpad.
=== modified file 'lib/lp/registry/browser/team.py'
--- lib/lp/registry/browser/team.py	2012-02-01 15:46:43 +0000
+++ lib/lp/registry/browser/team.py	2012-02-03 04:45:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
@@ -45,8 +45,9 @@
 
 from lazr.restful.interfaces import IJSONRequestCache
 from lazr.restful.utils import smartquote
+import pytz
 import simplejson
-import pytz
+from storm.expr import Join
 from z3c.ptcompat import ViewPageTemplateFile
 from zope.app.form.browser import TextAreaWidget
 from zope.component import getUtility
@@ -140,8 +141,14 @@
     ITeamMembershipSet,
     TeamMembershipStatus,
     )
+from lp.registry.model.commercialsubscription import CommercialSubscription
+from lp.registry.model.person import Person
+from lp.registry.model.product import Product
+from lp.registry.model.teammembership import TeamParticipation
 from lp.security import ModerateByRegistryExpertsOrAdmins
 from lp.services.config import config
+from lp.services.database.lpstorm import IStore
+from lp.services.features import getFeatureFlag
 from lp.services.fields import PublicPersonChoice
 from lp.services.identity.interfaces.emailaddress import IEmailAddressSet
 from lp.services.privacy.interfaces import IObjectPrivacy
@@ -221,8 +228,11 @@
 class TeamFormMixin:
     """Form to be used on forms which conditionally display team visibility.
 
-    The visibility field should only be shown to users with
-    launchpad.Commercial permission on the team.
+    The visibility field is shown if
+    * The user has launchpad.Commercial permission.
+    * Or the feature flag
+    disclosure.show_visibility_for_team_add.enabled is on, and the user has
+    a current commercial subscription.
     """
     field_names = [
         "name", "visibility", "displayname", "contactemail",
@@ -265,9 +275,31 @@
                     'Private teams must have a Restricted subscription '
                     'policy.')
 
-    def conditionallyOmitVisibility(self):
+    def conditionallyOmitVisibility(self, user):
         """Remove the visibility field if not authorized."""
-        if not check_permission('launchpad.Commercial', self.context):
+        if check_permission('launchpad.Commercial', self.context):
+            return None
+        if getFeatureFlag(
+            'disclosure.show_visibility_for_team_add.enabled'):
+            store = IStore(Person)
+            person = store.using(
+                Person,
+                Join(
+                    TeamParticipation,
+                    Person.id == TeamParticipation.personID),
+                Join(
+                    Product, TeamParticipation.teamID == Product._ownerID),
+                Join(
+                    CommercialSubscription,
+                    CommercialSubscription.productID == Product.id)
+                ).find(
+                    Person,
+                    CommercialSubscription.date_expires > datetime.now(
+                        pytz.UTC),
+                    Person.id == user.id)
+            if person.is_empty():
+                self.form_fields = self.form_fields.omit('visibility')
+        else:
             self.form_fields = self.form_fields.omit('visibility')
 
 
@@ -301,7 +333,7 @@
         self.field_names.remove('contactemail')
         self.field_names.remove('teamowner')
         super(TeamEditView, self).setUpFields()
-        self.conditionallyOmitVisibility()
+        self.conditionallyOmitVisibility(self.user)
 
     def setUpWidgets(self):
         super(TeamEditView, self).setUpWidgets()
@@ -997,7 +1029,7 @@
         Only Launchpad Admins get to see the visibility field.
         """
         super(TeamAddView, self).setUpFields()
-        self.conditionallyOmitVisibility()
+        self.conditionallyOmitVisibility(self.user)
 
     @action('Create Team', name='create')
     def create_action(self, action, data):

=== modified file 'lib/lp/registry/browser/tests/test_team.py'
--- lib/lp/registry/browser/tests/test_team.py	2012-01-30 19:36:57 +0000
+++ lib/lp/registry/browser/tests/test_team.py	2012-02-03 04:45:24 +0000
@@ -1,10 +1,16 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
 
+from datetime import (
+    datetime,
+    timedelta,
+    )
+
 import contextlib
 from lazr.restful.interfaces import IJSONRequestCache
+import pytz
 import simplejson
 import transaction
 from zope.component import getUtility
@@ -13,10 +19,12 @@
 from lp.registry.browser.team import (
     TeamIndexMenu,
     TeamOverviewMenu,
+    TeamMailingListArchiveView,
     )
 from lp.registry.interfaces.mailinglist import MailingListStatus
 from lp.registry.interfaces.person import (
     CLOSED_TEAM_POLICY,
+    IPersonSet,
     OPEN_TEAM_POLICY,
     PersonVisibility,
     TeamMembershipRenewalPolicy,
@@ -27,7 +35,8 @@
     ITeamMembershipSet,
     TeamMembershipStatus,
     )
-from lp.registry.browser.team import TeamMailingListArchiveView
+from lp.registry.model.commercialsubscription import CommercialSubscription
+from lp.services.features.testing import FeatureFixture
 from lp.services.propertycache import get_property_cache
 from lp.services.webapp.authorization import check_permission
 from lp.services.webapp.publisher import canonical_url
@@ -470,6 +479,83 @@
         self.assertEqual(['name'], view.field_names)
 
 
+class TestTeamAddView(TestCaseWithFactory):
+
+    layer = LaunchpadFunctionalLayer
+    view_name = '+newteam'
+    feature_flag = {'disclosure.show_visibility_for_team_add.enabled': 'on'}
+
+    def test_random_does_not_see_visibility_field(self):
+        personset = getUtility(IPersonSet)
+        person = self.factory.makePerson()
+        view = create_initialized_view(
+            personset, name=self.view_name, principal=person)
+        self.assertNotIn(
+            'visibility', [field.__name__ for field in view.form_fields])
+
+    def test_admin_sees_visibility_field(self):
+        personset = getUtility(IPersonSet)
+        admin = login_celebrity('admin')
+        view = create_initialized_view(
+            personset, name=self.view_name, principal=admin)
+        self.assertIn(
+            'visibility', [field.__name__ for field in view.form_fields])
+        
+    def test_random_does_not_see_visibility_field_with_flag(self):
+        personset = getUtility(IPersonSet)
+        person = self.factory.makePerson()
+        with person_logged_in(person):
+            with FeatureFixture(self.feature_flag):
+                view = create_initialized_view(
+                    personset, name=self.view_name, principal=person)
+                self.assertNotIn(
+                    'visibility',
+                    [field.__name__ for field in view.form_fields])
+
+    def create_subscription(self, product, expired=False):
+        if expired:
+            expiry = datetime.now(pytz.UTC) - timedelta(days=1)
+        else:
+            expiry = datetime.now(pytz.UTC) + timedelta(days=30)
+        CommercialSubscription(
+            product=product,
+            date_starts=datetime.now(pytz.UTC) - timedelta(days=90),
+            date_expires=expiry,
+            registrant=product.owner,
+            purchaser=product.owner,
+            sales_system_id='new',
+            whiteboard='')
+
+    def test_person_with_cs_sees_visibility_field_with_flag(self):
+        personset = getUtility(IPersonSet)
+        team = self.factory.makeTeam(
+            subscription_policy=TeamSubscriptionPolicy.MODERATED)
+        product = self.factory.makeProduct(owner=team)
+        self.create_subscription(product)
+        with person_logged_in(team.teamowner):
+            with FeatureFixture(self.feature_flag):
+                view = create_initialized_view(
+                    personset, name=self.view_name, principal=team.teamowner)
+                self.assertIn(
+                    'visibility',
+                    [field.__name__ for field in view.form_fields])
+
+
+    def test_person_with_expired_cs_does_not_see_visibility(self):
+        personset = getUtility(IPersonSet)
+        team = self.factory.makeTeam(
+            subscription_policy=TeamSubscriptionPolicy.MODERATED)
+        product = self.factory.makeProduct(owner=team)
+        self.create_subscription(product, expired=True)
+        with person_logged_in(team.teamowner):
+            with FeatureFixture(self.feature_flag):
+                view = create_initialized_view(
+                    personset, name=self.view_name, principal=team.teamowner)
+                self.assertNotIn(
+                    'visibility',
+                    [field.__name__ for field in view.form_fields])
+
+
 class TestTeamMenu(TestCaseWithFactory):
 
     layer = DatabaseFunctionalLayer

=== modified file 'lib/lp/services/features/flags.py'
--- lib/lp/services/features/flags.py	2012-01-13 11:12:41 +0000
+++ lib/lp/services/features/flags.py	2012-02-03 04:45:24 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2012 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __all__ = [
@@ -270,6 +270,13 @@
      '',
      '',
      ''),
+    ('disclosure.show_visibility_for_team_add.enabled',
+     'boolean',
+     ('If true, will show the visibility field for IPersonSet:+newteam if '
+      ' the user has a current commercial subscription.'),
+     '',
+     '',
+     ''),
     ])
 
 # The set of all flag names that are documented.