← Back to team overview

launchpad-reviewers team mailing list archive

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

 

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

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  #603353 Prevent new Private Membership Teams from being created
  https://bugs.launchpad.net/bugs/603353


= Summary =

Private membership teams are deprecated.  This branch will make it
impossible to create new ones.  After it has landed we'll change the
production database to transition existing PMTs to private teams.  At
that point a database patch can be landed to remove the enum and
completely remove all mention of PMTs.

== Proposed fix ==

Change the UI to not allow PMTs to be selected.  Add a model-level
validator to ensure none are created via the webservice.  Fix a ton of
tests.

== Pre-implementation notes ==

Chats with Curtis.

== Implementation details ==

As above.

== Tests ==

Run all registry and bug tests.

== Demo and Q/A ==

Try to create a new team and verify on 'Public' and 'Private' are available.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/browser/tests/peoplemerge-views.txt
  lib/lp/registry/tests/test_mlists.py
  lib/lp/registry/stories/teammembership/00-newteam.txt
  lib/lp/registry/tests/test_user_vocabularies.py
  lib/lp/registry/doc/private-team-roles.txt
  lib/lp/registry/browser/team.py
  lib/lp/registry/doc/message-holds-xmlrpc.txt
  lib/lp/registry/browser/tests/mailinglist-views.txt
  lib/lp/registry/doc/mailinglist-subscriptions-xmlrpc.txt
  lib/lp/registry/doc/vocabularies.txt
  lib/lp/registry/browser/tests/private-team-creation-views.txt
  lib/lp/registry/stories/mailinglists/lifecycle.txt
  lib/lp/registry/doc/mailinglist-xmlrpc.txt
  lib/lp/bugs/browser/tests/test_bugsupervisor.py
  lib/lp/bugs/stories/webservice/xx-bug-target.txt
  lib/lp/registry/doc/mailinglists.txt
  lib/lp/registry/doc/person.txt
  lib/lp/registry/interfaces/person.py
  lib/lp/registry/browser/tests/team-views.txt
  lib/lp/registry/model/person.py
  lib/lp/registry/tests/test_person.py
  lib/lp/registry/stories/webservice/xx-private-team.txt

./lib/lp/registry/tests/test_mlists.py
      35: E301 expected 1 blank line, found 0
./lib/lp/registry/stories/teammembership/00-newteam.txt
       0: narrative uses a moin header.
       2: narrative uses a moin header.
      84: narrative uses a moin header.
     122: source exceeds 78 characters.
./lib/lp/registry/browser/team.py
     128: E241 multiple spaces after ','
./lib/lp/registry/doc/message-holds-xmlrpc.txt
      54: Could not compile:
             finally:
./lib/lp/registry/doc/vocabularies.txt
       0: narrative uses a moin header.
      21: narrative uses a moin header.
     178: narrative uses a moin header.
     222: narrative uses a moin header.
     308: narrative uses a moin header.
     333: narrative uses a moin header.
     491: narrative uses a moin header.
     505: narrative uses a moin header.
     539: narrative uses a moin header.
     580: narrative uses a moin header.
     598: narrative uses a moin header.
     688: narrative uses a moin header.
     707: narrative uses a moin header.
     739: narrative uses a moin header.
     953: narrative uses a moin header.
    1054: narrative uses a moin header.
    1105: narrative uses a moin header.
    1147: narrative uses a moin header.
    1191: narrative uses a moin header.
    1252: narrative uses a moin header.
    1318: narrative uses a moin header.
    1352: narrative uses a moin header.
./lib/lp/registry/browser/tests/private-team-creation-views.txt
       0: narrative uses a moin header.
     174: narrative uses a moin header.
     221: narrative uses a moin header.
     366: narrative uses a moin header.
     273: local variable 'priv_subscription' is assigned to but never used
     315: local variable 'subscription' is assigned to but never used
./lib/lp/bugs/stories/webservice/xx-bug-target.txt
       0: narrative uses a moin header.
       6: narrative uses a moin header.
      46: narrative uses a moin header.
     121: narrative uses a moin header.
     162: narrative uses a moin header.
./lib/lp/registry/interfaces/person.py
     453: E302 expected 2 blank lines, found 1
    2132: E302 expected 2 blank lines, found 1
./lib/lp/registry/browser/tests/team-views.txt
       0: narrative uses a moin header.
       2: narrative uses a moin header.
      67: narrative uses a moin header.
     102: narrative uses a moin header.
     212: narrative uses a moin header.
     233: narrative uses a moin header.
     260: narrative uses a moin header.
     286: narrative uses a moin header.
./lib/lp/registry/model/person.py
     189: E302 expected 2 blank lines, found 3
     722: E222 multiple spaces after operator
    1444: E222 multiple spaces after operator
    1608: E231 missing whitespace after ','
    1641: W602 deprecated form of raising exception
    1821: E202 whitespace before ']'
    2034: W602 deprecated form of raising exception
    2082: E203 whitespace before ':'
    2114: W602 deprecated form of raising exception
    2220: E501 line too long (80 characters)
    2223: E501 line too long (80 characters)
    2709: E231 missing whitespace after ','
    2730: E202 whitespace before ')'
    2744: E202 whitespace before ')'
    2744: E231 missing whitespace after ','
    2767: E202 whitespace before ')'
    2811: E202 whitespace before ')'
    2821: E202 whitespace before ')'
    2828: E202 whitespace before ')'
    2833: E202 whitespace before ')'
    2838: E202 whitespace before ')'
    2852: E202 whitespace before ')'
    2857: E202 whitespace before ')'
    2857: E231 missing whitespace after ','
    3453: E202 whitespace before ')'
    3475: E231 missing whitespace after ','
    3480: E231 missing whitespace after ','
    3484: E231 missing whitespace after ','
    3565: E202 whitespace before ')'
    3574: E202 whitespace before ')'
    3899: E301 expected 1 blank line, found 0
    3959: E202 whitespace before ')'
    2220: Line exceeds 78 characters.
    2223: Line exceeds 78 characters.
./lib/lp/registry/stories/webservice/xx-private-team.txt
       0: narrative uses a moin header.
      78: narrative uses a moin header.

Most of these lint problems are not correct.  Will try again when the
linter is fixed.
-- 
https://code.launchpad.net/~bac/launchpad/bug-603353/+merge/29903
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~bac/launchpad/bug-603353 into lp:launchpad/devel.
=== modified file 'lib/lp/bugs/browser/tests/test_bugsupervisor.py'
--- lib/lp/bugs/browser/tests/test_bugsupervisor.py	2010-06-14 18:08:51 +0000
+++ lib/lp/bugs/browser/tests/test_bugsupervisor.py	2010-07-14 16:21:15 +0000
@@ -9,14 +9,11 @@
 
 from zope.app.form.interfaces import ConversionError
 
-import transaction
-
 from lp.bugs.browser.bugsupervisor import BugSupervisorEditSchema
 from lp.registry.interfaces.person import PersonVisibility
 from lp.testing import login, login_person, TestCaseWithFactory
 from lp.testing.views import create_initialized_view
 from canonical.testing import DatabaseFunctionalLayer
-from canonical.launchpad.fields import PrivateMembershipTeamNotAllowed
 
 
 class TestBugSupervisorEditView(TestCaseWithFactory):
@@ -106,23 +103,6 @@
         self.assertEqual([], view.errors)
         self.assertEqual(private_team, self.product.bug_supervisor)
 
-    def test_owner_cannot_appoint_his_private_membership_team(self):
-        private_membership_team = self.factory.makeTeam(
-            owner=self.owner,
-            visibility=PersonVisibility.PRIVATE_MEMBERSHIP)
-        # Commit the transaction so the team will exist after the failed view
-        # initialization calls abort.
-        transaction.commit()
-        login('admin@xxxxxxxxxxxxx')
-        form = self._makeForm(private_membership_team)
-        view = create_initialized_view(
-            self.product, name='+bugsupervisor', form=form)
-        self.assertTrue(
-            type(view.errors[0].errors) is PrivateMembershipTeamNotAllowed)
-        self.assertEqual("You must choose a valid person or team to be the "
-                         "bug supervisor for <boing />.",
-                         view.errors[1])
-
     def test_owner_cannot_appoint_another_team(self):
         team = self.factory.makeTeam(name='smack', displayname='<smack />')
         form = self._makeForm(team)
@@ -194,22 +174,6 @@
         self.assertEqual([], view.errors)
         self.assertEqual(private_team, self.product.bug_supervisor)
 
-    def test_admin_cannot_appoint_private_membership_team(self):
-        private_membership_team = self.factory.makeTeam(
-            visibility=PersonVisibility.PRIVATE_MEMBERSHIP)
-        # Commit the transaction so the team will exist after the failed view
-        # initialization calls abort.
-        transaction.commit()
-        login('admin@xxxxxxxxxxxxx')
-        form = self._makeForm(private_membership_team)
-        view = create_initialized_view(
-            self.product, name='+bugsupervisor', form=form)
-        self.assertTrue(
-            type(view.errors[0].errors) is PrivateMembershipTeamNotAllowed)
-        self.assertEqual("You must choose a valid person or team to be the "
-                         "bug supervisor for &lt;boing /&gt;.",
-                         view.errors[1])
-
 
 def test_suite():
     return unittest.TestLoader().loadTestsFromName(__name__)

=== modified file 'lib/lp/bugs/stories/webservice/xx-bug-target.txt'
--- lib/lp/bugs/stories/webservice/xx-bug-target.txt	2010-04-27 23:39:43 +0000
+++ lib/lp/bugs/stories/webservice/xx-bug-target.txt	2010-07-14 16:21:15 +0000
@@ -46,8 +46,8 @@
 
 == Official Bug Tags ==
 
-We can access official bug tag targets and add and remove tags. We create a new
-product, owned by ~salgado.
+We can access official bug tag targets and add and remove tags. We
+create a new product, owned by ~salgado.
 
     >>> from zope.component import getUtility
     >>> from canonical.launchpad.interfaces import IPersonSet
@@ -57,7 +57,8 @@
     >>> product = factory.makeProduct(name='tags-test-product', owner=salgado)
     >>> logout()
 
-The webserice client is logged in as salgado, so we can add a new official tag.
+The webserice client is logged in as salgado, so we can add a new
+official tag.
 
     >>> print webservice.named_post(
     ...     '/tags-test-product', 'addOfficialBugTag',
@@ -130,7 +131,7 @@
     ...     '/firefox', 'application/json',
     ...     dumps({'bug_supervisor_link': firefox_project['owner_link']}))
     HTTP/1.1 209 Content Returned...
-    
+
     >>> firefox_project = webservice.get('/firefox').jsonBody()
     >>> print firefox_project['bug_supervisor_link']
     http://api.launchpad.dev/beta/~name12
@@ -145,7 +146,7 @@
     ...     '/ubuntutest', 'application/json',
     ...     dumps({'bug_supervisor_link': ubuntutest_dist['owner_link']}))
     HTTP/1.1 209 Content Returned...
-    
+
     >>> ubuntutest_dist = webservice.get('/ubuntutest').jsonBody()
     >>> print ubuntutest_dist['bug_supervisor_link']
     http://api.launchpad.dev/beta/~ubuntu-team
@@ -171,7 +172,7 @@
     ...     '/firefox', 'application/json',
     ...     dumps({'security_contact_link': firefox_project['owner_link']}))
     HTTP/1.1 209 Content Returned...
-    
+
     >>> firefox_project = webservice.get('/firefox').jsonBody()
     >>> print firefox_project['security_contact_link']
     http://api.launchpad.dev/beta/~name12
@@ -186,7 +187,7 @@
     ...     '/ubuntutest', 'application/json',
     ...     dumps({'security_contact_link': ubuntutest_dist['owner_link']}))
     HTTP/1.1 209 Content Returned...
-    
+
     >>> ubuntutest_dist = webservice.get('/ubuntutest').jsonBody()
     >>> print ubuntutest_dist['security_contact_link']
     http://api.launchpad.dev/beta/~ubuntu-team
@@ -199,4 +200,3 @@
     HTTP/1.1 401 Unauthorized
     ...
     <BLANKLINE>
-

=== modified file 'lib/lp/registry/browser/team.py'
--- lib/lp/registry/browser/team.py	2010-05-21 17:04:28 +0000
+++ lib/lp/registry/browser/team.py	2010-07-14 16:21:15 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
@@ -166,6 +166,28 @@
         """Remove the visibility field if not authorized."""
         if not check_permission('launchpad.Commercial', self.context):
             self.form_fields = self.form_fields.omit('visibility')
+        else:
+            # XXX: BradCrittenden 2010-07-12 bug=602773:  This code can be
+            # removed when PRIVATE_MEMBERSHIP disappears fully.
+
+            # Remove the visibility selector and replace with one with a more
+            # limited vocabulary.
+            terms = [SimpleTerm(PersonVisibility.PUBLIC,
+                                PersonVisibility.PUBLIC.name,
+                                PersonVisibility.PUBLIC.title),
+                     SimpleTerm(PersonVisibility.PRIVATE,
+                                PersonVisibility.PRIVATE.name,
+                                PersonVisibility.PRIVATE.title),
+                     ]
+            visibility = self.form_fields['visibility'].field
+            field = Choice(
+                __name__=visibility.getName(),
+                title=visibility.title,
+                source=SimpleVocabulary(terms))
+            self.form_fields = (
+                self.form_fields.omit('visibility') +
+                form.Fields(field))
+            self.form_fields = self.form_fields.select(*self.field_names)
 
 
 class TeamEditView(TeamFormMixin, HasRenewalPolicyMixin,

=== modified file 'lib/lp/registry/browser/tests/mailinglist-views.txt'
--- lib/lp/registry/browser/tests/mailinglist-views.txt	2009-12-21 20:10:24 +0000
+++ lib/lp/registry/browser/tests/mailinglist-views.txt	2010-07-14 16:21:15 +0000
@@ -180,7 +180,7 @@
 Privacy and mailing lists
 =========================
 
-If a non-public team has a mailing list the information cannot be seen
+If a private team has a mailing list the information cannot be seen
 by non-members.  Access to the team index page is forbidden and the
 stanza for the mailing list is not shown.  NB:  the view class
 generates an Unauthorized exception which is turned into a NotFound in
@@ -190,12 +190,12 @@
     >>> owner = person_set.getByEmail('owner@xxxxxxxxxxxxx')
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> from lp.registry.interfaces.person import PersonVisibility
-    >>> pmt, pmt_list = factory.makeTeamAndMailingList(
+    >>> private_team, private_team_list = factory.makeTeamAndMailingList(
     ...     owner_name='owner',
     ...     team_name='private-team',
-    ...     visibility=PersonVisibility.PRIVATE_MEMBERSHIP)
+    ...     visibility=PersonVisibility.PRIVATE)
     >>> login(ANONYMOUS)
-    >>> view = create_initialized_view(pmt, '+index')
+    >>> view = create_initialized_view(private_team, '+index')
     Traceback (most recent call last):
      ...
     Unauthorized: (<Person at ...
@@ -203,22 +203,22 @@
 The owner of a team can see information about the mailing list.
 
     >>> login('owner@xxxxxxxxxxxxx')
-    >>> view = create_initialized_view(pmt, '+index')
+    >>> view = create_initialized_view(private_team, '+index')
     >>> print view.archive_url
     http://lists.launchpad.dev/private-team
 
 A non-owner member can see information about the mailing list.
 
-    >>> ignored = pmt.addMember(sample_person, owner)
+    >>> ignored = private_team.addMember(sample_person, owner)
     >>> login('test@xxxxxxxxxxxxx')
-    >>> view = create_initialized_view(pmt, '+index')
+    >>> view = create_initialized_view(private_team, '+index')
     >>> print view.archive_url
     http://lists.launchpad.dev/private-team
 
 An unprivileged non-member cannot see information about the mailing list.
 
     >>> login('no-priv@xxxxxxxxxxxxx')
-    >>> view = create_initialized_view(pmt, '+index')
+    >>> view = create_initialized_view(private_team, '+index')
     Traceback (most recent call last):
      ...
     Unauthorized: (<Person at ...
@@ -226,6 +226,6 @@
 An administrator may see the mailing list information.
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> view = create_initialized_view(pmt, '+index')
+    >>> view = create_initialized_view(private_team, '+index')
     >>> print view.archive_url
     http://lists.launchpad.dev/private-team

=== modified file 'lib/lp/registry/browser/tests/peoplemerge-views.txt'
--- lib/lp/registry/browser/tests/peoplemerge-views.txt	2010-05-13 22:45:12 +0000
+++ lib/lp/registry/browser/tests/peoplemerge-views.txt	2010-07-14 16:21:15 +0000
@@ -291,18 +291,3 @@
     >>> for notification in view.request.response.notifications:
     ...     print notification.message
     Team deleted.
-
-Private membership teams can be deleted by admins, and when they are all
-deleted, we can remove this visibility type.
-
-    >>> login('commercial-member@xxxxxxxxxxxxx')
-    >>> pm_team = factory.makeTeam(
-    ...     name='sekret', visibility=PersonVisibility.PRIVATE_MEMBERSHIP)
-    >>> login('admin@xxxxxxxxxxxxx')
-    >>> form = {'field.actions.delete': 'Delete'}
-    >>> view = create_initialized_view(pm_team, '+delete', form=form)
-    >>> view.errors
-    []
-    >>> for notification in view.request.response.notifications:
-    ...     print notification.message
-    Team deleted.

=== modified file 'lib/lp/registry/browser/tests/private-team-creation-views.txt'
--- lib/lp/registry/browser/tests/private-team-creation-views.txt	2010-05-27 02:19:27 +0000
+++ lib/lp/registry/browser/tests/private-team-creation-views.txt	2010-07-14 16:21:15 +0000
@@ -1,13 +1,14 @@
-= Private and Private Membership Team Creation =
+= Private Team Creation =
 
-Private and Private membership teams may only be created by commercial
-admins (launchpad.Commercial permission).  This permission is
-controlled in the UI by only displaying the 'visibility' control to
-commercial admins.
+Private teams may only be created by commercial admins
+(launchpad.Commercial permission).  This permission is controlled in
+the UI by only displaying the 'visibility' control to commercial
+admins.
 
 Unprivileged users do not see the visibility widget.
 
     >>> from lp.registry.interfaces.person import IPersonSet
+    >>> from lp.registry.interfaces.person import PersonVisibility
     >>> personset = getUtility(IPersonSet)
     >>> nopriv = personset.getByEmail('no-priv@xxxxxxxxxxxxx')
     >>> login('no-priv@xxxxxxxxxxxxx')
@@ -40,8 +41,8 @@
     >>> 'visibility' in [field.__name__ for field in view.form_fields]
     True
 
-When creating a private membership team, the team subscription policy
-must be 'Restricted.'
+When creating a private team, the team subscription policy must be
+'Restricted.'
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> foo_bar = personset.getByEmail('foo.bar@xxxxxxxxxxxxx')
@@ -53,33 +54,7 @@
     ...     'field.defaultrenewalperiod': '365',
     ...     'field.subscriptionpolicy': 'OPEN',
     ...     'field.renewal_policy': 'NONE',
-    ...     'field.visibility': 'Private Membership',
-    ...     'field.actions.create': 'Create',
-    ...     }
-    >>> view = create_initialized_view(
-    ...     personset, '+newteam',
-    ...     form=form, principal=foo_bar)
-
-    >>> print len(view.request.notifications)
-    0
-
-    >>> for error in view.errors:
-    ...     print error
-    Private teams must have a Restricted subscription policy.
-
-Likewise for a private team.
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> foo_bar = personset.getByEmail('foo.bar@xxxxxxxxxxxxx')
-    >>> form = {
-    ...     'field.name': 'super-secret2',
-    ...     'field.displayname': 'Shhhh',
-    ...     'field.description': 'my own team description',
-    ...     'field.defaultmembershipperiod': '365',
-    ...     'field.defaultrenewalperiod': '365',
-    ...     'field.subscriptionpolicy': 'OPEN',
-    ...     'field.renewal_policy': 'NONE',
-    ...     'field.visibility': 'Private',
+    ...     'field.visibility': 'PRIVATE',
     ...     'field.actions.create': 'Create',
     ...     }
     >>> view = create_initialized_view(
@@ -103,7 +78,7 @@
     ...     'field.defaultrenewalperiod': '365',
     ...     'field.subscriptionpolicy': 'RESTRICTED',
     ...     'field.renewal_policy': 'NONE',
-    ...     'field.visibility': 'Private',
+    ...     'field.visibility': 'PRIVATE',
     ...     'field.actions.create': 'Create',
     ...     }
     >>> view = create_initialized_view(
@@ -126,7 +101,7 @@
     ...     'field.defaultrenewalperiod': '365',
     ...     'field.subscriptionpolicy': 'RESTRICTED',
     ...     'field.renewal_policy': 'NONE',
-    ...     'field.visibility': 'Private Membership',
+    ...     'field.visibility': 'PRIVATE',
     ...     'field.actions.create': 'Create',
     ...     }
     >>> view = create_initialized_view(
@@ -143,52 +118,21 @@
     >>> transaction.commit()
     >>> secret_team = personset.getByName('secret-team')
     >>> print secret_team.visibility.name
-    PRIVATE_MEMBERSHIP
-
-Admins can successfully create a private membership team.
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> form = {
-    ...     'field.name': 'super-secret',
-    ...     'field.displayname': 'Shhhh',
-    ...     'field.description': 'my own team description',
-    ...     'field.defaultmembershipperiod': '365',
-    ...     'field.defaultrenewalperiod': '365',
-    ...     'field.subscriptionpolicy': 'RESTRICTED',
-    ...     'field.renewal_policy': 'NONE',
-    ...     'field.visibility': 'Private Membership',
-    ...     'field.actions.create': 'Create',
-    ...     }
-    >>> view = create_initialized_view(
-    ...     personset, '+newteam',
-    ...     form=form, principal=foo_bar)
-
-    >>> print len(view.request.notifications)
-    0
-
-    >>> print len(view.errors)
-    0
-
-    >>> import transaction
-    >>> transaction.commit()
-    >>> super_secret = personset.getByName('super-secret')
-    >>> print super_secret.visibility.name
-    PRIVATE_MEMBERSHIP
-
+    PRIVATE
 
 Admins who attempt to create a new team with the name of an existing
 team get the normal error message.
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> form = {
-    ...     'field.name': 'super-secret',
+    ...     'field.name': 'secret-team',
     ...     'field.displayname': 'Shhhh',
     ...     'field.description': 'my own team description',
     ...     'field.defaultmembershipperiod': '365',
     ...     'field.defaultrenewalperiod': '365',
     ...     'field.subscriptionpolicy': 'RESTRICTED',
     ...     'field.renewal_policy': 'NONE',
-    ...     'field.visibility': 'Private Membership',
+    ...     'field.visibility': 'PRIVATE',
     ...     'field.actions.create': 'Create',
     ...     }
     >>> view = create_initialized_view(
@@ -200,14 +144,14 @@
 
     >>> for error in view.errors:
     ...     print view.getFieldError(error.field_name)
-    super-secret is already in use by another person or team.
+    secret-team is already in use by another person or team.
 
 Regular users who try to create a team with a name that is already
 taken by a private team get the blacklist message.
 
     >>> login('no-priv@xxxxxxxxxxxxx')
     >>> form = {
-    ...     'field.name': 'super-secret',
+    ...     'field.name': 'secret-team',
     ...     'field.displayname': 'Shhhh',
     ...     'field.description': 'my own team description',
     ...     'field.defaultmembershipperiod': '365',
@@ -225,7 +169,7 @@
 
     >>> for error in view.errors:
     ...     print view.getFieldError(error.field_name)
-    The name 'super-secret' has been blocked by the Launchpad administrators.
+    The name 'secret-team' has been blocked by the Launchpad administrators.
 
 
 = Private Team Editing =
@@ -248,7 +192,7 @@
     >>> 'visibility' in [field.__name__ for field in view.form_fields]
     True
 
-And a non-public team must have restricted membership.
+And a private team must have restricted membership.
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> foo_bar = personset.getByEmail('foo.bar@xxxxxxxxxxxxx')
@@ -261,11 +205,11 @@
     ...     'field.defaultrenewalperiod': '365',
     ...     'field.subscriptionpolicy': 'OPEN',
     ...     'field.renewal_policy': 'NONE',
-    ...     'field.visibility': 'Private Membership',
+    ...     'field.visibility': 'PRIVATE',
     ...     'field.actions.save': 'Save',
     ...     }
     >>> view = create_initialized_view(
-    ...     super_secret, '+edit',
+    ...     secret_team, '+edit',
     ...     form=form, principal=foo_bar)
 
     >>> print len(view.request.notifications)
@@ -280,11 +224,9 @@
 A team can only change visibility if the new state will not leak any
 data or put the team into an inconsistent state.  The rules are:
 
-Private -> anything: Fail
-Private Membership -> Private : OK
-Private Membership -> Public : OK
-Public -> Private/PMT : OK, if the team only has the permitted artifacts
-Public -> Private/PMT : Fail, if the team has a restricted artifact
+Private -> Public: Fail
+Public -> Private : OK, if the team only has the permitted artifacts
+Public -> Private : Fail, if the team has a restricted artifact
 
 Private teams cannot be made public.
 
@@ -297,7 +239,7 @@
     ...     'field.defaultrenewalperiod': '365',
     ...     'field.subscriptionpolicy': 'OPEN',
     ...     'field.renewal_policy': 'NONE',
-    ...     'field.visibility': 'Public',
+    ...     'field.visibility': 'PUBLIC',
     ...     'field.actions.save': 'Save',
     ...     }
     >>> view = create_initialized_view(
@@ -322,74 +264,6 @@
     >>> print super_secret2.displayname
     Shhhh
 
-Private teams cannot become private membership.
-
-    >>> form = {
-    ...     'field.name': 'super-secret-two',
-    ...     'field.displayname': 'New Display Name',
-    ...     'field.description': 'my own team description',
-    ...     'field.defaultmembershipperiod': '365',
-    ...     'field.defaultrenewalperiod': '365',
-    ...     'field.subscriptionpolicy': 'RESTRICTED',
-    ...     'field.renewal_policy': 'NONE',
-    ...     'field.visibility': 'Private Membership',
-    ...     'field.actions.save': 'Save',
-    ...     }
-    >>> view = create_initialized_view(
-    ...     super_secret2, '+edit',
-    ...     form=form, principal=foo_bar)
-
-    >>> for notification in view.request.notifications:
-    ...     print notification.message
-    A private team cannot change visibility.
-
-    >>> print len(view.errors)
-    0
-
-Private Membership teams can be made private.
-
-    >>> from lp.registry.interfaces.person import PersonVisibility
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> team = factory.makeTeam(
-    ...     owner=foo_bar,
-    ...     visibility=PersonVisibility.PRIVATE_MEMBERSHIP)
-
-    >>> form = {
-    ...     'field.subscriptionpolicy': 'RESTRICTED',
-    ...     'field.visibility': 'Private',
-    ...     'field.actions.save': 'Save',
-    ...     }
-    >>> view = create_initialized_view(
-    ...     team, '+edit',
-    ...     form=form, principal=foo_bar)
-
-    >>> print len(view.request.notifications)
-    0
-    >>> print len(view.errors)
-    0
-
-Private Membership teams can be made public.
-
-    >>> from lp.registry.interfaces.person import PersonVisibility
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> team = factory.makeTeam(
-    ...     owner=foo_bar,
-    ...     visibility=PersonVisibility.PRIVATE_MEMBERSHIP)
-
-    >>> form = {
-    ...     'field.subscriptionpolicy': 'RESTRICTED',
-    ...     'field.visibility': 'Public',
-    ...     'field.actions.save': 'Save',
-    ...     }
-    >>> view = create_initialized_view(
-    ...     team, '+edit',
-    ...     form=form, principal=foo_bar)
-
-    >>> print len(view.request.notifications)
-    0
-    >>> print len(view.errors)
-    0
-
 Public teams can be made private if the only artifacts they have are
 those permitted by private teams.
 
@@ -433,8 +307,9 @@
     ...     private_archive.private = True
     ...     # A private PPA subscription.
     ...     login('foo.bar@xxxxxxxxxxxxx')
-    ...     another_team = factory.makeTeam(owner=team_owner,
-    ...                                     visibility=PersonVisibility.PRIVATE)
+    ...     another_team = factory.makeTeam(
+    ...         owner=team_owner,
+    ...         visibility=PersonVisibility.PRIVATE)
     ...     # We must login as the archive owner to add the subscription.
     ...     login_person(team_owner)
     ...     subscription = private_archive.newSubscription(
@@ -452,7 +327,7 @@
 
     >>> # With all of those artifacts in place we can still transition to private.
     >>> form = {
-    ...     'field.visibility': 'Private',
+    ...     'field.visibility': 'PRIVATE',
     ...     'field.subscriptionpolicy': 'RESTRICTED',
     ...     'field.actions.save': 'Save',
     ...     }
@@ -481,65 +356,19 @@
     >>> # With the addtion of hte bug tracker artifact the team will not
     >>> # be allowed to transition to Private.
     >>> form = {
-    ...     'field.visibility': 'Private',
-    ...     'field.subscriptionpolicy': 'RESTRICTED',
-    ...     'field.actions.save': 'Save',
-    ...     }
-    >>> view = create_initialized_view(
-    ...     team, '+edit',
-    ...     form=form, principal=foo_bar)
-    >>> print len(view.request.notifications)
-    0
-    >>> for error in view.errors:
-    ...     print error
-    This team cannot be converted to Private since it is referenced by a bugtracker.
-
-The artifacts we have for the team will prevent it from transitioning
-from Public to Private Membership.
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> team = factory.makeTeam(
-    ...     owner=foo_bar,
-    ...     visibility=PersonVisibility.PUBLIC)
-
-    >>> createTeamArtifacts(team, foo_bar)
-    >>> form = {
-    ...     'field.visibility': 'Private Membership',
-    ...     'field.subscriptionpolicy': 'RESTRICTED',
-    ...     'field.actions.save': 'Save',
-    ...     }
-    >>> view = create_initialized_view(
-    ...     team, '+edit',
-    ...     form=form, principal=foo_bar)
-
-    >>> print len(view.request.notifications)
-    0
-    >>> for error in view.errors:
-    ...     print error
-    This team cannot be converted to Private Membership since it is referenced by a branch, a branchsubscription,
-    a branchvisibilitypolicy, a bugsubscription, a bugtask and an archive.
-
-A team with no artifacts can be converted from Public to Private Membership.
-
-    >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> team = factory.makeTeam(
-    ...     owner=foo_bar,
-    ...     visibility=PersonVisibility.PUBLIC)
-
-    >>> form = {
-    ...     'field.visibility': 'Private Membership',
-    ...     'field.subscriptionpolicy': 'RESTRICTED',
-    ...     'field.actions.save': 'Save',
-    ...     }
-    >>> view = create_initialized_view(
-    ...     team, '+edit',
-    ...     form=form, principal=foo_bar)
-
-    >>> print len(view.request.notifications)
-    0
-    >>> print len(view.errors)
-    0
-
+    ...     'field.visibility': 'PRIVATE',
+    ...     'field.subscriptionpolicy': 'RESTRICTED',
+    ...     'field.actions.save': 'Save',
+    ...     }
+    >>> view = create_initialized_view(
+    ...     team, '+edit',
+    ...     form=form, principal=foo_bar)
+    >>> print len(view.request.notifications)
+    0
+    >>> for error in view.errors:
+    ...     print error
+    This team cannot be converted to Private since it is referenced by a
+    bugtracker.
 
 = Use of 'private-' prefix =
 
@@ -554,7 +383,7 @@
     ...     'field.defaultrenewalperiod': '365',
     ...     'field.subscriptionpolicy': 'RESTRICTED',
     ...     'field.renewal_policy': 'NONE',
-    ...     'field.visibility': 'Private',
+    ...     'field.visibility': 'PRIVATE',
     ...     'field.actions.create': 'Create',
     ...     }
     >>> view = create_initialized_view(
@@ -578,7 +407,7 @@
     ...     'field.defaultrenewalperiod': '365',
     ...     'field.subscriptionpolicy': 'RESTRICTED',
     ...     'field.renewal_policy': 'NONE',
-    ...     'field.visibility': 'Private',
+    ...     'field.visibility': 'PRIVATE',
     ...     'field.actions.create': 'Create',
     ...     }
     >>> view = create_initialized_view(
@@ -590,4 +419,5 @@
 
     >>> for error in view.errors:
     ...     print view.getFieldError(error.field_name)
-    The name 'private-top-secret' has been blocked by the Launchpad administrators.
+    The name 'private-top-secret' has been blocked by the Launchpad
+    administrators.

=== modified file 'lib/lp/registry/browser/tests/team-views.txt'
--- lib/lp/registry/browser/tests/team-views.txt	2010-01-06 13:42:17 +0000
+++ lib/lp/registry/browser/tests/team-views.txt	2010-07-14 16:21:15 +0000
@@ -284,19 +284,6 @@
     >>> print view.visibility_portlet_class
     portlet private
 
-Private membership teams are also indicated as private.
-
-    >>> private_membership_team = factory.makeTeam(
-    ...     owner=sample_person,
-    ...     name='private-membership-team',
-    ...     displayname='Private Membership Team',
-    ...     visibility=PersonVisibility.PRIVATE_MEMBERSHIP)
-    >>> view = create_initialized_view(private_membership_team, '+index')
-    >>> print view.visibility_info
-    Team membership is viewable by team members
-    >>> print view.visibility_portlet_class
-    portlet private
-
 == +add-my-teams ==
 
 This page lists the teams that you administer and can add as a member
@@ -309,15 +296,3 @@
     ...     print candidate.name, candidate.visibility.title
     landscape-developers Public
     launchpad-users Public
-
-Making one of the teams that sample person owns public will cause it
-to show up.
-
-    >>> removeSecurityProxy(private_membership_team).visibility = (
-    ...     PersonVisibility.PUBLIC)
-    >>> view = create_initialized_view(guadamen, '+add-my-teams')
-    >>> for candidate in view.candidate_teams:
-    ...     print candidate.name, candidate.visibility.title
-    landscape-developers Public
-    launchpad-users Public
-    private-membership-team Public

=== modified file 'lib/lp/registry/doc/mailinglist-subscriptions-xmlrpc.txt'
--- lib/lp/registry/doc/mailinglist-subscriptions-xmlrpc.txt	2009-05-06 15:13:39 +0000
+++ lib/lp/registry/doc/mailinglist-subscriptions-xmlrpc.txt	2010-07-14 16:21:15 +0000
@@ -269,7 +269,8 @@
 the lists, the archive address is hard-coded to not be registered in
 Launchpad.
 
-    >>> mailinglist_api.isRegisteredInLaunchpad(config.mailman.archive_address)
+    >>> mailinglist_api.isRegisteredInLaunchpad(
+    ...     config.mailman.archive_address)
     False
 
 This is true even if by dumb luck the address actually gets registered in
@@ -281,7 +282,8 @@
     ...     EmailAddressStatus.VALIDATED, account=fred.account)
     >>> transaction.commit()
 
-    >>> mailinglist_api.isRegisteredInLaunchpad(config.mailman.archive_address)
+    >>> mailinglist_api.isRegisteredInLaunchpad(
+    ...     config.mailman.archive_address)
     False
 
 The Mail Archive is only used for public team mailing lists.  If the team
@@ -298,7 +300,7 @@
 Teams with mailing lists cannot change visibility, so the team must be made
 private before its mailing list is created.
 
-    >>> team_three.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
+    >>> team_three.visibility = PersonVisibility.PRIVATE
     >>> from canonical.launchpad.ftests import syncUpdate
     >>> syncUpdate(team_three)
     >>> queryAdapter(team_three, IObjectPrivacy).is_private

=== modified file 'lib/lp/registry/doc/mailinglist-xmlrpc.txt'
--- lib/lp/registry/doc/mailinglist-xmlrpc.txt	2009-12-04 17:16:39 +0000
+++ lib/lp/registry/doc/mailinglist-xmlrpc.txt	2010-07-14 16:21:15 +0000
@@ -103,7 +103,7 @@
     >>> mailinglist_api.getPendingActions()
     {}
 
-Launchpad supports private membership teams, which are also allowed to have
+Launchpad supports private teams, which are also allowed to have
 mailing lists.
 
     >>> from lp.registry.interfaces.person import (
@@ -111,7 +111,7 @@
     >>> team_private = factory.makeTeam(
     ...     no_priv, name='team-private',
     ...     subscription_policy=TeamSubscriptionPolicy.RESTRICTED,
-    ...     visibility=PersonVisibility.PRIVATE_MEMBERSHIP)
+    ...     visibility=PersonVisibility.PRIVATE)
     >>> list_private = list_set.new(team_private)
     >>> commit()
 
@@ -372,14 +372,14 @@
     >>> from lp.registry.interfaces.person import PersonVisibility
     >>> team_not_public = factory.makeTeam(
     ...     no_priv, name='team-not-public',
-    ...     visibility=PersonVisibility.PRIVATE_MEMBERSHIP)
+    ...     visibility=PersonVisibility.PRIVATE)
     >>> mailinglist_api.isTeamPublic('team-not-public')
     False
 
     # If the visibility of a public team is changed to anything other than
-    # public, it's not report as public anymore, just like if it were not
+    # public, it's not reported as public anymore, just like if it were not
     # public since its creation.
-    >>> team_public.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
+    >>> team_public.visibility = PersonVisibility.PRIVATE
     >>> mailinglist_api.isTeamPublic('team-public')
     False
 

=== modified file 'lib/lp/registry/doc/mailinglists.txt'
--- lib/lp/registry/doc/mailinglists.txt	2009-12-24 01:41:54 +0000
+++ lib/lp/registry/doc/mailinglists.txt	2010-07-14 16:21:15 +0000
@@ -274,7 +274,7 @@
 Permissions on a team's mailing list are not tracked separately from
 permissions on the team.
 
-You cannot turn a team with a mailing list into a private membership team.
+You cannot turn a team with a mailing list into a private team.
 
     >>> from zope.component import queryAdapter
     >>> from canonical.lazr.interfaces.objectprivacy import IObjectPrivacy
@@ -282,18 +282,18 @@
     >>> queryAdapter(team_one, IObjectPrivacy).is_private
     False
     >>> login('foo.bar@xxxxxxxxxxxxx')
-    >>> team_one.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
+    >>> team_one.visibility = PersonVisibility.PRIVATE
     Traceback (most recent call last):
     ...
     ImmutableVisibilityError: This team cannot be converted to
-    Private Membership since it is referenced by a mailing list.
+    Private since it is referenced by a mailing list.
 
-However, you can give a private membership team a mailing list.
+However, you can give a private team a mailing list.
 
     >>> thereminists = new_team('thereminists')
     >>> queryAdapter(thereminists, IObjectPrivacy).is_private
     False
-    >>> thereminists.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
+    >>> thereminists.visibility = PersonVisibility.PRIVATE
 
     >>> queryAdapter(thereminists, IObjectPrivacy).is_private
     True
@@ -303,15 +303,6 @@
     >>> thereminists_list
     <MailingList for team "thereminists"; status=ACTIVE at ...>
 
-Once a private team has a mailing list though, it cannot be made public.
-
-    >>> thereminists.visibility = PersonVisibility.PUBLIC
-    Traceback (most recent call last):
-    ...
-    ImmutableVisibilityError: This team cannot be made public since it has a
-    mailing list
-
-
 Welcome messages
 ================
 

=== modified file 'lib/lp/registry/doc/message-holds-xmlrpc.txt'
--- lib/lp/registry/doc/message-holds-xmlrpc.txt	2010-01-14 02:42:01 +0000
+++ lib/lp/registry/doc/message-holds-xmlrpc.txt	2010-07-14 16:21:15 +0000
@@ -176,11 +176,11 @@
     <DBItem PostedMessageStatus.DISCARDED, (70) Discarded>
 
 
-Private membership teams
-========================
+Private teams
+=============
 
-When a private membership team has a mailing list, its messages can also be
-held for moderator approval.
+When a private team has a mailing list, its messages can also be held
+for moderator approval.
 
     >>> from lp.registry.interfaces.person import (
     ...     IPersonSet, PersonVisibility, TeamSubscriptionPolicy)
@@ -188,7 +188,7 @@
     >>> team_private = factory.makeTeam(
     ...     no_priv, name='team-private',
     ...     subscription_policy=TeamSubscriptionPolicy.RESTRICTED,
-    ...     visibility=PersonVisibility.PRIVATE_MEMBERSHIP)
+    ...     visibility=PersonVisibility.PRIVATE)
 
     >>> from lp.registry.interfaces.mailinglist import IMailingListSet
     >>> list_private = getUtility(IMailingListSet).new(team_private)

=== modified file 'lib/lp/registry/doc/person.txt'
--- lib/lp/registry/doc/person.txt	2010-04-23 16:54:21 +0000
+++ lib/lp/registry/doc/person.txt	2010-07-14 16:21:15 +0000
@@ -688,21 +688,11 @@
 Team Visibility
 ...............
 
-A Team can have its visibility attribute set to PersonVisibility.PUBLIC,
-PersonVisibility.PRIVATE_MEMBERSHIP, or PersonVisibility.PRIVATE.
-
-A PRIVATE_MEMBERSHIP team is a team that exists for the sole purpose of
-providing a membership list for the purpose of single sign on, etc.
-PRIVATE_MEMBERSHIP teams can have mailing lists but cannot participate
-in Launchpad in other ways.
-
-Like PRIVATE_MEMBERSHIP teams, PRIVATE teams are hidden from view from
-non-members but they are allowed to actually do things in Launchpad.
-
-To prevent leaking of private membership information, private-membership
-teams are only allowed to be connected with a few different database
-objects. This is enforced in the model, but there are also checks in the
-form handling to provide clearer error messages.
+A Team can have its visibility attribute set to
+PersonVisibility.PUBLIC or PersonVisibility.PRIVATE.
+
+PRIVATE teams are hidden from view from non-members but they are
+allowed to actually do things in Launchpad.
 
 The PublicPersonChoice for interface classes and the
 validate_public_person for database classes only allow public teams to
@@ -712,9 +702,7 @@
 invalid team is passed to the constructor or is used to set one of the
 attributes.
 
-myteam is a private-membership team, so validate_public_person will
-reject it for the BugSubscription.person and
-BugSubscription.subscribed_by attributes.
+Private teams can be subscribed to bugs.
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> from lp.bugs.interfaces.bug import IBugSet
@@ -723,25 +711,8 @@
     >>> from canonical.launchpad.database import BugSubscription
     >>> person_set = getUtility(IPersonSet)
     >>> bug_set = getUtility(IBugSet)
-    >>> myteam = person_set.getByName('myteam')
+    >>> bug = bug_set.get(1)
     >>> guadamen = person_set.getByName('guadamen')
-    >>> bug = bug_set.get(1)
-    >>> bug_subscription = BugSubscription(bug=bug, person=myteam,
-    ...     subscribed_by=guadamen)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=myteam, visibility=PRIVATE_MEMBERSHIP) to <BugSubscription...
-
-    >>> bug_subscription = BugSubscription(bug=bug, person=guadamen,
-    ...                                    subscribed_by=myteam)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=myteam, visibility=PRIVATE_MEMBERSHIP) to <BugSubscription...
-
-Private teams can be subscribed to bugs.
-
     >>> salgado = personset.getByName('salgado')
     >>> private_team = factory.makeTeam(salgado, name='private-team',
     ...     displayname='Private Team',
@@ -755,11 +726,7 @@
     ...     subscribed_by=private_team)
 
 Teams also have a 'private' attribute that is true if the team is
-private membership or private and false for public teams.  It is also
-false for people.
-
-    >>> myteam.private
-    True
+private and false for public teams.  It is also false for people.
 
     >>> private_team.private
     True
@@ -770,24 +737,6 @@
     >>> salgado.private
     False
 
-The visibility attribute may only be modified if the user has
-launchpad.Commercial permissions.
-
-    >>> login('commercial-member@xxxxxxxxxxxxx')
-    >>> print myteam.visibility.name
-    PRIVATE_MEMBERSHIP
-
-    >>> myteam.visibility = PersonVisibility.PUBLIC
-    >>> print myteam.visibility.name
-    PUBLIC
-
-    >>> login('no-priv@xxxxxxxxxxxxx')
-    >>> myteam.visibility = PersonVisibility.PRIVATE
-    Traceback (most recent call last):
-      ...
-    Unauthorized: ...
-
-
 Latest Team Memberships
 -----------------------
 
@@ -858,7 +807,7 @@
 The teams returned are dependent upon the team's visibility (privacy)
 and whether the logged in user is a member of those teams.
 
-Anonymous users cannot see non-public teams, such as 'myteam'.
+Anonymous users cannot see non-public teams, such as 'private-team'.
 
     >>> login(ANONYMOUS)
     >>> print_people(personset.find('team'))
@@ -866,7 +815,6 @@
     Hoary Gnome Team (name21): []
     HWDB Team (hwdb-team): []
     Just a new team (new-team): []
-    My Team (myteam): []
     No Team Memberships (no-team-memberships):
       [u'no-team-memberships@xxxxxxxx']
     Other Team (otherteam): []
@@ -882,16 +830,16 @@
 
 But Owner, a member of that team, will see it in the results.
 
-    >>> login('owner@xxxxxxxxxxxxx')
+    >>> login_person(private_team.teamowner)
     >>> print_people(personset.find('team'))
     Another a new team (new3): []
     Hoary Gnome Team (name21): []
     HWDB Team (hwdb-team): []
     Just a new team (new-team): []
-    My Team (myteam): []
     No Team Memberships (no-team-memberships):
       [u'no-team-memberships@xxxxxxxx']
     Other Team (otherteam): []
+    Private Team (private-team): []
     Simple Team (simple-team): []
     Team Membership Janitor (team-membership-janitor): []
     testing Spanish team (testing-spanish-team): []
@@ -962,7 +910,6 @@
     Hoary Gnome Team (name21): []
     HWDB Team (hwdb-team): []
     Just a new team (new-team): []
-    My Team (myteam): []
     Other Team (otherteam): []
     Simple Team (simple-team): []
     testing Spanish team (testing-spanish-team): []
@@ -1561,5 +1508,3 @@
 
     >>> team.is_valid_person
     False
-
-

=== modified file 'lib/lp/registry/doc/private-team-roles.txt'
--- lib/lp/registry/doc/private-team-roles.txt	2010-05-27 02:04:21 +0000
+++ lib/lp/registry/doc/private-team-roles.txt	2010-07-14 16:21:15 +0000
@@ -1,20 +1,8 @@
 Private team participation
 ==========================
 
-During the transitional period between having only public and private
-membership team visibility until the end goal of having only public
-and private team visibility, there will be a time when teams can have a
-visibility set to one of the three: public, private membership, and
-private.
-
-Public teams can perform any role in Launchpad.  Private membership
-teams are very restricted in the roles they may play.  Private teams
-can play more roles.  Private membership teams were originally
-implemented in this manner since it was the most expedient way to
-provide the required functionality and nothing more.  With the advent
-of truly private teams their utility is increased but with more
-safeguards to prevent potential data leakage.
-
+Public teams can perform any role in Launchpad.  Private teams
+have some restrictions in the roles they can perform.
 
 Bugs
 ====
@@ -30,24 +18,12 @@
     >>> priv_team = factory.makeTeam(name='private-team',
     ...     owner=team_owner,
     ...     visibility=PersonVisibility.PRIVATE)
-    >>> pm_team = factory.makeTeam(name='private-membership-team',
-    ...     owner=team_owner,
-    ...     visibility=PersonVisibility.PRIVATE_MEMBERSHIP)
 
 A private team can be subscribed to a bug.
 
     >>> bug = factory.makeBug()
     >>> priv_subscription = bug.subscribe(priv_team, team_owner)
 
-But a private membership team cannot.
-
-    >>> pm_subscription = bug.subscribe(pm_team, team_owner)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <BugSubscription...
-
 A private team can subscribe others to a bug (the `subscribed_by`
 person).
 
@@ -64,15 +40,6 @@
 
     >>> bug.unsubscribe(team_owner, team_owner)
 
-A private membership team cannot subscribe others to a bug.
-
-    >>> pm_subscription = bug.subscribe(team_owner, pm_team)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <BugSubscription...
-
 
 Bug task assignee
 -----------------
@@ -84,16 +51,6 @@
     >>> print bugtask.assignee.name
     private-team
 
-However a private membership team is not allowed to be a bugtask assignee.
-
-    >>> bugtask.transitionToAssignee(pm_team)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <BugTask...
-
-
 Branches
 ========
 
@@ -105,17 +62,6 @@
     >>> branch = factory.makeBranch()
     >>> branch.setOwner(priv_team, user=admin_user)
 
-But private membership teams cannot own a branch.
-
-    >>> branch = factory.makeBranch()
-    >>> branch.setOwner(pm_team, user=admin_user)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <Branch...
-
-
 Branch subscriptions
 --------------------
 
@@ -134,19 +80,6 @@
     >>> print subscription.person.name
     private-team
 
-Private membership teams cannot subscribe to branches.
-
-    >>> branch = factory.makeBranch()
-    >>> subscription = branch.subscribe(
-    ...     pm_team,
-    ...     BranchSubscriptionNotificationLevel.DIFFSONLY,
-    ...     BranchSubscriptionDiffSize.WHOLEDIFF,
-    ...     CodeReviewNotificationLevel.STATUS, team_owner)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <BranchSubscription at...
 
 Branch visibility policies
 --------------------------
@@ -171,16 +104,6 @@
     >>> print_team_policies(evolution)
     Private Team: Private
 
-But private membership teams cannot.
-
-    >>> evolution.setBranchVisibilityTeamPolicy(
-    ...     pm_team, BranchVisibilityRule.PRIVATE)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <BranchVisibilityTeamPolicy at...
-
 
 PPAs
 ====
@@ -202,18 +125,6 @@
     ...     distribution=ubuntu, name='private-team-archive',
     ...     require_virtualized=False)
 
-Alas, Private Membership Teams cannot own PPAs.
-
-    >>> noarchive = archive_set.new(
-    ...     owner=pm_team, purpose=ArchivePurpose.PPA,
-    ...     distribution=ubuntu, name='pmt-archive',
-    ...     require_virtualized=False)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <Archive at...
-
 
 PPA subscriptions
 -----------------
@@ -232,17 +143,6 @@
     ...     registrant=team_owner)
     >>> transaction.commit()
 
-Private Membership Teams cannot subscribe to private PPAs.
-
-    >>> nosubscription = private_archive.newSubscription(subscriber=pm_team,
-    ...     registrant=team_owner)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <...ArchiveSubscriber object at...
-    >>> transaction.abort()
-
 
 Structural Subscriptions
 ========================
@@ -260,16 +160,6 @@
     >>> sub.bug_notification_level
     <DBItem BugNotificationLevel.NOTHING, (10) Nothing>
 
-Private membership teams cannot.
-
-    >>> sub = firefox.addSubscription(
-    ...     subscriber=pm_team, subscribed_by=team_owner)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <...StructuralSubscription at...
-
 
 Structural Subscription to Distributions
 ----------------------------------------
@@ -284,16 +174,6 @@
     >>> sub.bug_notification_level
     <DBItem BugNotificationLevel.NOTHING, (10) Nothing>
 
-Private membership teams cannot.
-
-    >>> sub = ubuntu.addSubscription(
-    ...     subscriber=pm_team, subscribed_by=team_owner)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <...StructuralSubscription at...
-
 
 Project Roles
 =============
@@ -318,63 +198,33 @@
     (name=private-team, visibility=PRIVATE) to
     <Product at...
 
-    >>> product = factory.makeProduct(registrant=pm_team)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <Product at...
-
 
 Maintainer/Owner
 ----------------
 
-A public team and a private team can be a project owner but a private
-membership team cannot.
+A public team and a private team can be a project owner.
 
     >>> # The registrant must be specified or it will default to the owner.
     >>> product = factory.makeProduct(registrant=admin_user)
     >>> product.owner = public_team
     >>> product.owner = priv_team
-    >>> product.owner = pm_team
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <Product at...
 
 Driver
 ------
 
-A public team and a private team can be a project driver but a private
-membership team cannot.
+A public team and a private team can be a project driver.
 
     >>> product = factory.makeProduct()
     >>> product.driver = priv_team
 
-    >>> product.driver = pm_team
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <Product at...
-
-
 Bug Supervisor
 --------------
 
-A public team and a private team can be a project bug supervisor but a
-private membership team cannot.
+A public team and a private team can be a project bug supervisor.
 
     >>> product = factory.makeProduct()
     >>> product.setBugSupervisor(public_team, admin_user)
     >>> product.setBugSupervisor(priv_team, admin_user)
-    >>> product.setBugSupervisor(pm_team, admin_user)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <Product at...
 
 
 Product Series Roles
@@ -384,39 +234,24 @@
 Owner
 -----
 
-A public team and a private team can be a product series owner but a
-private membership team cannot.
+A public team and a private team can be a product series owner.
 
     >>> product = factory.makeProduct(registrant=admin_user,
     ...                               owner=public_team)
     >>> product_series = factory.makeProductSeries(product, owner=public_team)
     >>> product_series = factory.makeProductSeries(product, owner=priv_team)
 
-    >>> product_series = factory.makeProductSeries(product, owner=pm_team)
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <ProductSeries at...
-
 
 Driver
 ------
 
-A public team and a private team can be a product series driver but a
-private membership team cannot.
+A public team and a private team can be a product series driver.
 
     >>> product = factory.makeProduct(registrant=admin_user,
     ...                               owner=public_team)
     >>> product_series = factory.makeProductSeries(product, owner=public_team)
     >>> product_series.driver = public_team
     >>> product_series.driver = priv_team
-    >>> product_series.driver = pm_team
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <ProductSeries at...
 
 
 Product Release Roles
@@ -425,8 +260,7 @@
 Owner
 -----
 
-A public team and a private team can be a product series owner but a
-private membership team cannot.
+A public team and a private team can be a product series owner.
 
     >>> product = factory.makeProduct(registrant=admin_user,
     ...                               owner=public_team)
@@ -437,12 +271,6 @@
     ...     product=product, milestone=product_milestone)
     >>> product_release.owner = public_team
     >>> product_release.owner = priv_team
-    >>> product_release.owner = pm_team
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <ProductRelease at...
 
 Some artifacts of a product change ownership when the product owner
 changes.  The artifacts are product series, product release, and
@@ -460,12 +288,6 @@
     ...      productseries=product_series)
     >>> product.owner = public_team
     >>> product.owner = priv_team
-    >>> product.owner = pm_team
-    Traceback (most recent call last):
-    ...
-    PrivatePersonLinkageError: Cannot link person
-    (name=private-membership-team, visibility=PRIVATE_MEMBERSHIP) to
-    <Product at...
 
 
 Team Membership
@@ -491,22 +313,16 @@
 
     >>> public = PersonVisibility.PUBLIC
     >>> private = PersonVisibility.PRIVATE
-    >>> private_membership = PersonVisibility.PRIVATE_MEMBERSHIP
 
-    >>> visibility_list = [public, private, private_membership]
-    >>> for joined in PersonVisibility.items:
-    ...     for joiner in PersonVisibility.items:
+    >>> visibility_list = list(PersonVisibility.items)
+    >>> visibility_list.remove(PersonVisibility.PRIVATE_MEMBERSHIP)
+    >>> for joined in visibility_list:
+    ...     for joiner in visibility_list:
     ...         join_team(joined, joiner)
     ...     print "---"
     Public <- Public:  Allowed
-    Public <- Private Membership:  Not Allowed
     Public <- Private:  Not Allowed
     ---
-    Private Membership <- Public:  Allowed
-    Private Membership <- Private Membership:  Not Allowed
-    Private Membership <- Private:  Not Allowed
-    ---
     Private <- Public:  Allowed
-    Private <- Private Membership:  Not Allowed
     Private <- Private:  Not Allowed
     ---

=== modified file 'lib/lp/registry/doc/vocabularies.txt'
--- lib/lp/registry/doc/vocabularies.txt	2010-05-18 17:43:58 +0000
+++ lib/lp/registry/doc/vocabularies.txt	2010-07-14 16:21:15 +0000
@@ -258,9 +258,9 @@
 membership is public.
 
     >>> from canonical.launchpad.interfaces import PersonVisibility
-    >>> otherteam = person_set.getByName('otherteam')
-    >>> ignored = otherteam.addMember(foo_bar, foo_bar)
-    >>> otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
+    >>> pubteam = factory.makeTeam(owner=foo_bar, name='public-team',
+    ...                            displayname="Public Team",
+    ...                            visibility=PersonVisibility.PUBLIC)
     >>> for term in person_active_membership:
     ...     print term.token, term.value.displayname, term.title
     canonical-partner-dev Canonical Partner Developers
@@ -270,13 +270,13 @@
     admins Launchpad Administrators Launchpad Administrators
     launchpad-buildd-admins Launchpad Buildd Admins Launchpad Buildd Admins
     launchpad Launchpad Developers Launchpad Developers
+    public-team Public Team Public Team
     testing-spanish-team testing Spanish team testing Spanish team
     name18 Ubuntu Gnome Team Ubuntu Gnome Team
     ubuntu-team Ubuntu Team Ubuntu Team
     vcs-imports VCS imports VCS imports
 
-
-    >>> otherteam.visibility = PersonVisibility.PUBLIC
+    >>> pubteam.visibility = PersonVisibility.PRIVATE
     >>> for term in person_active_membership:
     ...     print term.token, term.value.displayname, term.title
     canonical-partner-dev Canonical Partner Developers
@@ -286,7 +286,6 @@
     admins Launchpad Administrators Launchpad Administrators
     launchpad-buildd-admins Launchpad Buildd Admins Launchpad Buildd Admins
     launchpad Launchpad Developers Launchpad Developers
-    otherteam Other Team Other Team
     testing-spanish-team testing Spanish team testing Spanish team
     name18 Ubuntu Gnome Team Ubuntu Gnome Team
     ubuntu-team Ubuntu Team Ubuntu Team
@@ -727,7 +726,8 @@
     >>> [(p.name, p.is_valid_person) for p in vocab.search('matsubara')]
     [(u'matsubara', False)]
 
-    >>> [(p.name, p.is_valid_person) for p in vocab.search('mark@xxxxxxxxxxx')]
+    >>> [(p.name, p.is_valid_person)
+    ...  for p in vocab.search('mark@xxxxxxxxxxx')]
     [(u'mark', True)]
 
     >>> [(p.name, getattr(p.teamowner, 'name', None))
@@ -812,15 +812,15 @@
      u'simple-team', u'testing-spanish-team', u'ubuntu-security',
      u'ubuntu-team', u'warty-gnome']
 
-The PRIVATE team is also displayed for Launchpad admins, as is the
-PRIVATE_MEMBERSHIP team 'myteam'.
+The PRIVATE team is also displayed for Launchpad admins.
 
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> sorted(person.name for person in vocab.search('team'))
     [u'hwdb-team', u'myteam', u'name18', u'name20', u'name21',
      u'no-team-memberships', u'otherteam',
-     u'private-team', u'simple-team', u'testing-spanish-team',
-     u'ubuntu-security', u'ubuntu-team', u'warty-gnome']
+     u'private-team', u'public-team', u'simple-team',
+     u'testing-spanish-team', u'ubuntu-security', u'ubuntu-team',
+     u'warty-gnome']
 
 The PRIVATE team can be looked up via getTermByToken for a member of the team.
 
@@ -1159,7 +1159,8 @@
     >>> people = vocab.search(None)
     >>> people.count() > 0
     True
-    >>> invalid_people = [person for person in people if not person.is_valid_person]
+    >>> invalid_people = [
+    ...     person for person in people if not person.is_valid_person]
     >>> print len(invalid_people)
     0
 
@@ -1168,7 +1169,8 @@
     >>> carlos_people = vocab.search('Carlos')
     >>> print len(list(carlos_people))
     1
-    >>> invalid_carlos = [person for person in carlos_people if not person.is_valid_person]
+    >>> invalid_carlos = [
+    ...     person for person in carlos_people if not person.is_valid_person]
     >>> print len(invalid_carlos)
     0
 
@@ -1409,7 +1411,8 @@
     >>> check_permission('launchpad.ProjectReview', product_set)
     True
 
-    >>> comm_proj_vocab = get_naked_vocab(registry_member, "CommercialProjects")
+    >>> comm_proj_vocab = get_naked_vocab(registry_member,
+    ...                                   "CommercialProjects")
     >>> print comm_proj_vocab.displayname
     Select a commercial project
 

=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py	2010-07-13 15:29:08 +0000
+++ lib/lp/registry/interfaces/person.py	2010-07-14 16:21:15 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 # pylint: disable-msg=E0211,E0213
@@ -1537,10 +1537,9 @@
     visibility = exported(
         Choice(title=_("Visibility"),
                description=_(
-                   "Public visibility is standard.  Private Membership "
-                   "means that a team's members are hidden.  "
+                   "Public visibility is standard.  "
                    "Private means the team is completely "
-                   "hidden [experimental]."),
+                   "hidden."),
                required=True, vocabulary=PersonVisibility,
                default=PersonVisibility.PUBLIC))
 

=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py	2010-07-12 08:45:32 +0000
+++ lib/lp/registry/model/person.py	2010-07-14 16:21:15 +0000
@@ -194,21 +194,18 @@
     * Prevent private teams from any transition.
     """
 
+    # XXX: BradCrittenden 2010-07-12 bug=602773: Private membership teams are
+    # deprecated and new ones may not be created.
+    if value == PersonVisibility.PRIVATE_MEMBERSHIP:
+        raise AssertionError(
+            "Private membership teams are deprecated.")
+
     # Prohibit any visibility changes for private teams.  This rule is
     # recognized to be Draconian and may be relaxed in the future.
     if person.visibility == PersonVisibility.PRIVATE:
         raise ImmutableVisibilityError(
             'A private team cannot change visibility.')
 
-    mailing_list = getUtility(IMailingListSet).get(person.name)
-
-    if (value == PersonVisibility.PUBLIC and
-        person.visibility == PersonVisibility.PRIVATE_MEMBERSHIP and
-        mailing_list is not None and
-        mailing_list.status != MailingListStatus.PURGED):
-        raise ImmutableVisibilityError(
-            'This team cannot be made public since it has a mailing list')
-
     # If transitioning to a non-public visibility, check for existing
     # relationships that could leak data.
     if value != PersonVisibility.PUBLIC:

=== modified file 'lib/lp/registry/stories/mailinglists/lifecycle.txt'
--- lib/lp/registry/stories/mailinglists/lifecycle.txt	2009-12-24 01:41:54 +0000
+++ lib/lp/registry/stories/mailinglists/lifecycle.txt	2010-07-14 16:21:15 +0000
@@ -191,7 +191,8 @@
 list was the team contact method, the contact method will be changed
 to 'each user individually'.
 
-    >>> browser.open('http://launchpad.dev/~landscape-developers/+mailinglist')
+    >>> browser.open(
+    ...     'http://launchpad.dev/~landscape-developers/+mailinglist')
     >>> browser.getControl('Deactivate this Mailing List').click()
     >>> browser.getLink(url='+contactaddress').click()
     >>> control = browser.getControl(name='field.contact_method')
@@ -199,7 +200,8 @@
     ['Each member individually']
 
     >>> act()
-    >>> browser.open('http://launchpad.dev/~landscape-developers/+mailinglist')
+    >>> browser.open(
+    ...     'http://launchpad.dev/~landscape-developers/+mailinglist')
     >>> print mailing_list_status_message(browser.contents)
     This team's mailing list has been deactivated.
 
@@ -247,7 +249,7 @@
     >>> from lp.registry.interfaces.person import PersonVisibility
     >>> login('foo.bar@xxxxxxxxxxxxx')
     >>> bassists = mailinglists_helper.new_team('bassists')
-    >>> bassists.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
+    >>> bassists.visibility = PersonVisibility.PRIVATE
     >>> bassists_list = mailinglists_helper.new_list_for_team(bassists)
     >>> logout()
 
@@ -258,8 +260,8 @@
     ...     find_tag_by_id(user_browser.contents, 'mailing-list-archive'))
     http://lists.launchpad.dev/bassists
 
-Anonymous users cannot see the link, because they cannot even see the private
-membership team.
+Anonymous users cannot see the link, because they cannot even see the
+private team.
 
     >>> anon_browser.open('http://launchpad.dev/~bassists')
     Traceback (most recent call last):

=== modified file 'lib/lp/registry/stories/teammembership/00-newteam.txt'
--- lib/lp/registry/stories/teammembership/00-newteam.txt	2009-07-23 13:44:13 +0000
+++ lib/lp/registry/stories/teammembership/00-newteam.txt	2010-07-14 16:21:15 +0000
@@ -85,7 +85,7 @@
 == As an administrator ==
 
 Administrators can create teams in much the same manner but they will
-also be given the ability to change the team's visibility
+also be given the ability to set the team's visibility
 
     >>> admin_browser.open('http://launchpad.dev/people/')
     >>> admin_browser.getLink('Register a team').click()
@@ -97,7 +97,7 @@
     'Register a new team in Launchpad'
 
     >>> admin_browser.getControl(name='field.name').value = 'super-secret'
-    >>> admin_browser.getControl('Visibility').value = ['PRIVATE_MEMBERSHIP']
+    >>> admin_browser.getControl('Visibility').value = ['PRIVATE']
     >>> admin_browser.getControl('Display Name').value = 'Shhhh'
     >>> admin_browser.getControl(
     ...     'Team Description').value = 'my own team description'
@@ -114,7 +114,7 @@
     >>> admin_browser.title
     'Shhhh in Launchpad'
 
-A team with Private Membership visibility must be Restricted.
+A team with Private visibility must be Restricted.
 
     >>> admin_browser.open('http://launchpad.dev/people/')
     >>> admin_browser.getLink('Register a team').click()
@@ -128,7 +128,7 @@
     >>> admin_browser.getControl('Open Team').selected = True
     >>> admin_browser.getControl(
     ...     name='field.subscriptionpolicy').value = ['OPEN']
-    >>> admin_browser.getControl('Visibility').value = ['PRIVATE_MEMBERSHIP']
+    >>> admin_browser.getControl('Visibility').value = ['PRIVATE']
     >>> admin_browser.getControl('Create').click()
     >>> for message in get_feedback_messages(admin_browser.contents):
     ...     print message

=== removed file 'lib/lp/registry/stories/teammembership/xx-private-membership.txt'
--- lib/lp/registry/stories/teammembership/xx-private-membership.txt	2010-06-07 22:52:12 +0000
+++ lib/lp/registry/stories/teammembership/xx-private-membership.txt	1970-01-01 00:00:00 +0000
@@ -1,415 +0,0 @@
-= Teams with Private Membership =
-
-If a team's visibility attribute is set to Private Membership, only
-Launchpad admins and members of that team can see the membership.
-
-Create a test team with private membership and a test member. Even
-though no-priv is a proposed member by requesting to join this moderated
-team, he still does not get access to the membership list.
-
-    >>> def print_members(contents, type):
-    ...     """Extract members of a team from the html output."""
-    ...     table = find_tag_by_id(contents, type)
-    ...     for link in table.findAll('a'):
-    ...         if link.renderContents() != 'Edit' and not link.find('img'):
-    ...             print link.renderContents()
-
-
-== Membership Page ==
-
-A team member should be able to view the +members page.
-
-    >>> browser = setupBrowser(auth='Basic member@xxxxxxxxxxxxx:test')
-    >>> browser.open('http://launchpad.dev/~myteam/+members')
-    >>> print_members(browser.contents, 'activemembers')
-    Gold Member
-    Owner
-    >>> print_members(browser.contents, 'proposedmembers')
-    No Privileges Person
-
-
-== Team Page ==
-
-The page indicates that the team is private.
-
-    >>> browser.open('http://launchpad.dev/~myteam')
-    >>> privacy_info = find_tag_by_id(browser.contents, 'privacy')
-    >>> print extract_text(privacy_info)
-    Team membership is viewable by team members
-
-A team member will be able to see these parts of the team page.
-
-    >>> find_tag_by_id(browser.contents, 'recently-approved')
-    <...<a href=".../~member" class="sprite person">Gold Member</a>...
-
-
-    >>> find_tag_by_id(browser.contents, 'recently-proposed')
-    <...<a href=".../~no-priv" class="sprite person">No Privileges Person</a>...
-
-    >>> print extract_text(
-    ...     find_tag_by_id(browser.contents, 'membership-summary'))
-    2 active members, 1 proposed member...
-
-
-== Team Participation ==
-
-=== Direct Membership ===
-
-The person overview page and the +assignedbugs page show which teams that
-person participates in. Although, it would be time consuming to gather the
-entire team membership by viewing all the members' person pages, we don't
-want to leak this information, so MyTeam won't show up on the person
-overview page or the +assignedbugs page because it has a private membership.
-
-    >>> user_browser.open('http://launchpad.dev/~owner')
-    >>> div = find_tag_by_id(user_browser.contents, 'participation')
-    >>> a_tags = div.findAll('a')
-    >>> for a_tag in a_tags:
-    ...     print a_tag.contents
-    [u'HWDB Team']
-    [u'Other Team']
-
-    >>> user_browser.open('http://launchpad.dev/~owner/+assignedbugs')
-    >>> div = find_tag_by_id(user_browser.contents,
-    ...                      'portlet-team-assigned-bugs')
-    >>> a_tags = div.findAll('a')
-    >>> for a_tag in a_tags:
-    ...     print a_tag.contents
-    [u'HWDB Team']
-    [u'Other Team']
-
-    >>> user_browser.open('http://launchpad.dev/~owner/+teamhierarchy')
-    >>> div = find_tag_by_id(user_browser.contents, 'superteams')
-    >>> a_tags = div.findAll('a')
-    >>> for a_tag in a_tags:
-    ...     print extract_text(a_tag)
-    HWDB Team
-    Other Team
-
-A member of a team with private membership (MyTeam) can see that team
-on other members' overview or +assignedbugs pages.
-
-    >>> browser.open('http://launchpad.dev/~owner')
-    >>> div = find_tag_by_id(browser.contents, 'participation')
-    >>> a_tags = div.findAll('a')
-    >>> for a_tag in a_tags:
-    ...     print a_tag.contents
-    [u'HWDB Team']
-    [u'Other Team']
-    [u'My Team']
-
-    >>> browser.open('http://launchpad.dev/~owner/+assignedbugs')
-    >>> div = find_tag_by_id(browser.contents,
-    ...                      'portlet-team-assigned-bugs')
-    >>> a_tags = div.findAll('a')
-    >>> for a_tag in a_tags:
-    ...     print a_tag.contents
-    [u'HWDB Team']
-    [u'My Team']
-    [u'Other Team']
-
-    >>> browser.open('http://launchpad.dev/~owner/+teamhierarchy')
-    >>> div = find_tag_by_id(browser.contents, 'superteams')
-    >>> a_tags = div.findAll('a')
-    >>> for a_tag in a_tags:
-    ...     print extract_text(a_tag)
-    HWDB Team
-    My Team
-    Other Team
-
-
-== Teams with Icons ==
-
-The person page also shows a list of icons for all the teams that
-person participates in, if the team has a custom icon. Only team
-members should be able to see their team's icon in this list.
-
-First, we will add a custom icon to two teams.
-
-    >>> from canonical.launchpad.ftests import set_branding
-    >>> admin_browser = setupBrowser(auth='Basic mark@xxxxxxxxxxx:test')
-    >>> admin_browser.open('http://launchpad.dev/~myteam/+branding')
-    >>> set_branding(admin_browser)
-    >>> admin_browser.getControl('Change Branding').click()
-
-    >>> admin_browser.open('http://launchpad.dev/~otherteam/+branding')
-    >>> set_branding(admin_browser)
-    >>> admin_browser.getControl('Change Branding').click()
-
-The user which is not on myteam will only see otherteam listed.
-
-    >>> user_browser.open('http://launchpad.dev/~owner')
-    >>> div = find_tag_by_id(user_browser.contents, 'teams_with_icons')
-    >>> for a_tag in div.findAll('a'):
-    ...     print a_tag['href']
-    /~otherteam
-
-
-Another team member will see both the icons (otherteam and myteam).
-
-    >>> browser.open('http://launchpad.dev/~owner')
-    >>> div = find_tag_by_id(browser.contents, 'teams_with_icons')
-    >>> for a_tag in div.findAll('a'):
-    ...     print a_tag['href']
-    /~myteam
-    /~otherteam
-
-
-== Restrict Answer Contact ==
-
-To prevent leaking information about a team's private membership,
-no user can add that team as an answer contact for a project.
-
-Even the owner of the team with private membership should not see
-MyTeam as an option in the +answer-contact form.
-
-    >>> owner_browser = setupBrowser(auth='Basic owner@xxxxxxxxxxxxx:test')
-    >>> owner_browser.open(
-    ...     'http://answers.launchpad.dev/ubuntu/+answer-contact')
-    >>> team_div = find_tag_by_id(owner_browser.contents,
-    ...                            'answer-contact-teams')
-    >>> for label in team_div.findAll('label'):
-    ...     print label.contents[1]
-    &nbsp;Other Team
-
-
-== Preventing Private Team Information Leakage ==
-
-To prevent leaking information about a team's private membership,
-no user can add that team as a member of a public team.
-
-    >>> admin_browser.open('http://launchpad.dev/~simple-team/+addmember')
-    >>> admin_browser.getControl('New member').value = 'myteam'
-    >>> admin_browser.getControl('Add Member').click()
-    >>> admin_browser.url
-    'http://launchpad.dev/%7Esimple-team/+addmember'
-    >>> get_feedback_messages(admin_browser.contents)
-    [...There is 1 error...
-    ...A private team is not allowed.']
-
-Anonymous users cannot even know that the private membership team even
-exists.
-
-    >>> anon_browser.open('http://launchpad.dev/~myteam')
-    Traceback (most recent call last):
-    ...
-    NotFound: Object: <...>, name: u'~myteam'
-
-Similarly, non-team members cannot even know that the private team exists.
-
-    >>> user_browser.open('http://launchpad.dev/~myteam/+members')
-    Traceback (most recent call last):
-    ...
-    NotFound: Object: <...>, name: u'~myteam'
-
-A public team can be made a member of a private team (but not vice versa).
-When this connection is made and members of the private team view the public
-team's home page, they see the private team show up in the "Subteam of"
-section.
-
-    >>> owner_browser.open('http://launchpad.dev/~myteam/+addmember')
-    >>> owner_browser.getControl('New member').value = 'guadamen'
-    >>> owner_browser.getControl('Add Member').click()
-    >>> admin_browser.open(
-    ...     'http://launchpad.dev/~guadamen/+invitation/myteam')
-    >>> admin_browser.getControl('Accept').click()
-
-    >>> browser.open('http://launchpad.dev/~guadamen')
-    >>> print extract_text(
-    ...     find_tag_by_id(browser.contents, 'subteam-of'))
-    Subteam of
-    &#8220;GuadaMen&#8221; is a member of these teams: My Team...
-
-However, to anonymous or non-members, the team name is hidden and there is
-no clickable link.
-
-    >>> user_browser.open('http://launchpad.dev/~guadamen')
-    >>> print extract_text(
-    ...     find_tag_by_id(user_browser.contents, 'subteam-of'))
-    Subteam of
-    &#8220;GuadaMen&#8221; is a member of these teams: &lt;hidden&gt;...
-    >>> user_browser.getLink('<hidden>')
-    Traceback (most recent call last):
-    ...
-    LinkNotFoundError
-
-    >>> anon_browser.open('http://launchpad.dev/~guadamen')
-    >>> print extract_text(
-    ...     find_tag_by_id(anon_browser.contents, 'subteam-of'))
-    Subteam of
-    &#8220;GuadaMen&#8221; is a member of these teams: &lt;hidden&gt;...
-    >>> anon_browser.getLink('<hidden>')
-    Traceback (most recent call last):
-    ...
-    LinkNotFoundError
-
-
-== Restrict Subscribing to Bugs ==
-
-To prevent leaking information about a team's private membership,
-no user can subscribe that team to a bug.
-
-    >>> admin_browser.open(
-    ...     'http://bugs.launchpad.dev/firefox/+bug/1/+addsubscriber')
-    >>> admin_browser.getControl('Person').value = 'myteam'
-    >>> admin_browser.getControl('Subscribe user').click()
-    >>> admin_browser.url
-    'http://bugs.launchpad.dev/firefox/+bug/1/+addsubscriber'
-    >>> get_feedback_messages(admin_browser.contents)
-    [...A private-membership team is not allowed...
-
-
-== Restrict Subscribing to Blueprints ==
-
-To prevent leaking information about a team's private membership,
-no user can subscribe that team to a blueprint.
-
-    >>> admin_browser.open('http://blueprints.launchpad.dev'
-    ...                    '/firefox/+spec/canvas/+addsubscriber')
-    >>> admin_browser.getControl('Subscriber').value = 'myteam'
-    >>> admin_browser.getControl('Continue').click()
-    >>> admin_browser.url
-    'http://blueprints.launchpad.dev/firefox/+spec/canvas/+addsubscriber'
-    >>> get_feedback_messages(admin_browser.contents)
-    [...A private team is not allowed...
-
-
-== Restrict Appointing a Translator ==
-
-To prevent leaking information about a team's private membership,
-no user can specify that team as a translator.
-
-    >>> admin_browser.open('http://translations.launchpad.dev'
-    ...                    '/+groups/testing-translation-team/+appoint')
-    >>> admin_browser.getControl('Language').value = ['de']
-    >>> admin_browser.getControl('Translator').value = 'myteam'
-    >>> admin_browser.getControl('Appoint').click()
-    >>> admin_browser.url
-    'http://translations.launchpad.dev/+groups/testing-translation-team/+appoint'
-    >>> get_feedback_messages(admin_browser.contents)
-    [...A private team is not allowed...
-
-
-== Restrict Project Group Owner ==
-
-To prevent leaking information about a team's private membership,
-no user can set that team as the owner/maintainer of a project.
-
-    >>> admin_browser.open('http://launchpad.dev/mozilla/+reassign')
-    >>> admin_browser.getControl(name='field.existing').value = ['existing']
-    >>> admin_browser.getControl(name='field.owner').value = 'myteam'
-    >>> admin_browser.getControl('Change').click()
-    >>> admin_browser.url
-    'http://launchpad.dev/mozilla/+reassign'
-    >>> get_feedback_messages(admin_browser.contents)
-    [...The person/team named 'myteam' is not a valid owner for...
-
-
-== Restrict Product Maintainer ==
-
-To prevent leaking information about a team's private membership,
-no user can set that team as the maintainer of a product.
-
-    >>> admin_browser.open('http://launchpad.dev/jokosher/+edit-people')
-    >>> admin_browser.getControl(name='field.owner').value = 'myteam'
-    >>> admin_browser.getControl('Save changes').click()
-    >>> admin_browser.url
-    'http://launchpad.dev/jokosher/+edit-people'
-    >>> get_feedback_messages(admin_browser.contents)
-    [...A private-membership team is not allowed...
-
-
-== Restrict Product Bug Supervisor ==
-
-To prevent leaking information about a team's private membership,
-no user can set that team as the bug supervisor of a product.
-
-    >>> admin_browser.open('http://launchpad.dev/jokosher/+bugsupervisor')
-    >>> admin_browser.getControl(name='field.bug_supervisor').value = 'myteam'
-    >>> admin_browser.getControl('Change').click()
-    >>> admin_browser.url
-    'http://launchpad.dev/jokosher/+bugsupervisor'
-    >>> get_feedback_messages(admin_browser.contents)
-    [...You must choose a valid person or team to be the bug supervisor for...
-
-
-== Restrict Distro Registrant ==
-
-To prevent leaking information about a team's private membership,
-no user can set that team as the registrant of a distribution.
-
-    >>> admin_browser.open('http://launchpad.dev/ubuntu/+reassign')
-    >>> admin_browser.getControl(name='field.owner').value = 'myteam'
-    >>> admin_browser.getControl('Change').click()
-    >>> admin_browser.url
-    'http://launchpad.dev/ubuntu/+reassign'
-    >>> get_feedback_messages(admin_browser.contents)
-    [...The person/team named 'myteam' is not a valid owner for...
-
-
-== Restrict Distro Bug Supervisor ==
-
-To prevent leaking information about a team's private membership,
-no user can set that team as the bug supervisor of a distribution.
-
-    >>> admin_browser.open('http://launchpad.dev/ubuntu/+bugsupervisor')
-    >>> admin_browser.getControl(name='field.bug_supervisor').value = 'myteam'
-    >>> admin_browser.getControl('Change').click()
-    >>> admin_browser.url
-    'http://launchpad.dev/ubuntu/+bugsupervisor'
-    >>> get_feedback_messages(admin_browser.contents)
-    [...You must choose a valid person or team to be the bug supervisor...
-
-
-== Edit the team's visibility ==
-
-A team cannot have its visibility set to private-membership unless the
-subscription policy is also set to restricted.
-
-    >>> admin_browser = setupBrowser(auth='Basic mark@xxxxxxxxxxx:test')
-    >>> admin_browser.open('http://launchpad.dev/~otherteam/+edit')
-    >>> admin_browser.getControl(name='field.subscriptionpolicy').value = [
-    ...     'OPEN']
-    >>> admin_browser.getControl('Visibility').value = [
-    ...     'PRIVATE_MEMBERSHIP']
-    >>> admin_browser.getControl('Save').click()
-    >>> for message in get_feedback_messages(admin_browser.contents):
-    ...     print message
-    There is 1 error.
-    Private teams must have a Restricted subscription policy.
-
-    >>> admin_browser.getControl(name='field.subscriptionpolicy').value = [
-    ...     'MODERATED']
-    >>> admin_browser.getControl('Save').click()
-    >>> for message in get_feedback_messages(admin_browser.contents):
-    ...     print message
-    There is 1 error.
-    Private teams must have a Restricted subscription policy.
-
-    >>> admin_browser.getControl(name='field.subscriptionpolicy').value = [
-    ...     'RESTRICTED']
-    >>> admin_browser.getControl('Save').click()
-    >>> for message in get_feedback_messages(admin_browser.contents):
-    ...     print message
-    >>> admin_browser.url
-    'http://launchpad.dev/~otherteam'
-
-Only Launchpad admins can set the visibility. Even the team owner can't do it.
-
-    >>> owner_browser.open('http://launchpad.dev/~otherteam/+edit')
-    >>> owner_browser.getControl('Visibility').value = [
-    ...     'PRIVATE_MEMBERSHIP']
-    Traceback (most recent call last):
-    ...
-    LookupError: label 'Visibility'
-
-The team owner also cannot set the subscription policy away from
-restricted, if the visibility is private-membership.
-
-    >>> owner_browser.getControl(name='field.subscriptionpolicy').value = [
-    ...     'MODERATED']
-    >>> owner_browser.getControl('Save').click()
-    >>> for message in get_feedback_messages(owner_browser.contents):
-    ...     print message
-    There is 1 error.
-    Private teams must have a Restricted subscription policy.

=== renamed file 'lib/lp/registry/stories/webservice/xx-private-membership.txt' => 'lib/lp/registry/stories/webservice/xx-private-team.txt'
--- lib/lp/registry/stories/webservice/xx-private-membership.txt	2009-12-24 01:38:11 +0000
+++ lib/lp/registry/stories/webservice/xx-private-team.txt	2010-07-14 16:21:15 +0000
@@ -1,36 +1,52 @@
-= Private memberships =
-
-Some teams may have private memberships, meaning that only the team
-members (and LP admins) can see who are the other members of the team.
-
-'myteam' is a team with private memberships, so someone who's not a
-team member can't see, for instance, the membership of a person on that
-team.
+= Private teams =
+
+Some teams may be private, meaning that only the team members (and LP
+admins) can see the team and who are the other members of the team.
+
+A regular user cannot get information about a private team and
+Launchpad returns a NotFound error to disguise the fact that the team
+even exists.
+
+    >>> login('test@xxxxxxxxxxxxx')
+    >>> from lp.registry.interfaces.person import PersonVisibility
+    >>> team_owner = factory.makePerson(name="private-team-owner")
+    >>> logout()
+    >>> login_person(team_owner)
+    >>> member1 = factory.makePerson(name='member-one')
+    >>> private_team = factory.makeTeam(
+    ...     owner=team_owner,
+    ...     name="private-team",
+    ...     visibility=PersonVisibility.PRIVATE)
+    >>> response = private_team.addMember(member1, team_owner)
+    >>> import transaction
+    >>> transaction.commit()
+    >>> logout()
 
     # XXX: 2008-08-01, salgado: Notice how the total_size is incorrect here.
     # That ought to be fixed at some point.
-    >>> member = user_webservice.get("/~member").jsonBody()
+    >>> member = user_webservice.get("/~private-team-owner").jsonBody()
     >>> response = user_webservice.get(
     ...     member['memberships_details_collection_link'])
     >>> print sorted(response.jsonBody().items())
-    [(u'entries', [{...}]),
+    [(u'entries', []),
      (u'resource_type_link',
       u'http://.../#team_membership-page-resource'),
      (u'start', 0),
-     (u'total_size', 2)]
-
-Salgado can, though, because he's a Launchpad admin.
-
+     (u'total_size', 1)]
+
+    Salgado can see the team since he's a Launchpad admin.
+
+    >>> member = webservice.get("/~private-team-owner").jsonBody()
     >>> response = webservice.get(
     ...     member['memberships_details_collection_link'])
     >>> print sorted(response.jsonBody().items())
     [(u'entries',
-      [{u'status': u'Approved',...
-        u'team_link': u'http://.../~myteam'...
+      [{u'status': u'Administrator',...
+        u'team_link': u'http://.../~private-team'...
      (u'resource_type_link',
       u'http://.../#team_membership-page-resource'),
      (u'start', 0),
-     (u'total_size', 2)]
+     (u'total_size', 1)]
 
 Similarly, when a public team is a sub-team of a private team, non-members
 cannot see the private team in the public team's super_team's attribute.
@@ -103,7 +119,7 @@
     ...                    headers)
 
     >>> print modify_team(
-    ...     '/~my-new-team', {'visibility' : 'Private Membership'},
+    ...     '/~my-new-team', {'visibility' : 'Private'},
     ...     'PATCH', comm_webservice)
     HTTP/1.1 209 Content Returned
     ...
@@ -114,12 +130,25 @@
 
     >>> team = webservice.get('/~my-new-team').jsonBody()
     >>> print team['visibility']
-    Private Membership
-
-As an admin, Salgado can also change the team's visibility.
+    Private
+
+As an admin, Salgado can also change a team's visibility.
+
+    >>> print user_webservice.named_post(
+    ...    "/people", "newTeam", {},
+    ...    name='my-new-team-2', display_name="My New Team 2")
+    HTTP/1.1 201 Created
+    ...
+    Location: http://.../~my-new-team-2
+    ...
+    >>> team = user_webservice.get('/~my-new-team-2').jsonBody()
+    >>> print team['self_link']
+    http://api.launchpad.dev/.../~my-new-team-2
+    >>> print team['visibility']
+    Public
 
     >>> print modify_team(
-    ...     '/~my-new-team', {'visibility' : 'Public'},
+    ...     '/~my-new-team-2', {'visibility' : 'Private'},
     ...     'PATCH', webservice)
     HTTP/1.1 209 Content Returned
     ...
@@ -128,13 +157,26 @@
     <BLANKLINE>
     {...}
 
-    >>> team = webservice.get('/~my-new-team').jsonBody()
+    >>> team = webservice.get('/~my-new-team-2').jsonBody()
     >>> print team['visibility']
-    Public
+    Private
 
 An unprivileged user is not able to change the visibility.
 
-    >>> print modify_team('/~my-new-team', {'visibility' : 'Private Membership'},
+    >>> print user_webservice.named_post(
+    ...    "/people", "newTeam", {},
+    ...    name='my-new-team-3', display_name="My New Team 3")
+    HTTP/1.1 201 Created
+    ...
+    Location: http://.../~my-new-team-3
+    ...
+    >>> team = user_webservice.get('/~my-new-team-3').jsonBody()
+    >>> print team['self_link']
+    http://api.launchpad.dev/.../~my-new-team-3
+    >>> print team['visibility']
+    Public
+
+    >>> print modify_team('/~my-new-team-3', {'visibility' : 'Private'},
     ...     'PATCH', user_webservice)
     HTTP/1.1 401 Unauthorized
     ...

=== modified file 'lib/lp/registry/tests/test_mlists.py'
--- lib/lp/registry/tests/test_mlists.py	2010-02-16 15:25:52 +0000
+++ lib/lp/registry/tests/test_mlists.py	2010-07-14 16:21:15 +0000
@@ -7,7 +7,6 @@
 
 
 import os
-import sys
 import errno
 import tempfile
 import unittest
@@ -31,6 +30,7 @@
 
 factory = LaunchpadObjectFactory()
 
+
 class CapturingLogger(FakeLogger):
     def __init__(self):
         self.io = StringIO()
@@ -130,7 +130,7 @@
             'dperson@xxxxxxxxxxx',
             'elly.person@xxxxxxxxxxx (Elly Q. Person)',
             ))
-        self.assertPeople(u'anne', u'bart', u'cris', u'dave', u'elly',)
+        self.assertPeople(u'anne', u'bart', u'cris', u'dave', u'elly')
         self.assertAddresses(
             u'anne.person@xxxxxxxxxxx', u'bperson@xxxxxxxxxxx',
             u'cris.person@xxxxxxxxxxx', u'dperson@xxxxxxxxxxx',
@@ -476,12 +476,12 @@
     def _makeList(self, name, owner):
         self.team, self.mailing_list = factory.makeTeamAndMailingList(
             name, owner,
-            visibility=PersonVisibility.PRIVATE_MEMBERSHIP,
+            visibility=PersonVisibility.PRIVATE,
             subscription_policy=TeamSubscriptionPolicy.RESTRICTED)
 
     def test_simple_import_membership(self):
         # Test the import of a list/team membership to a restricted, private
-        # membership team.
+        # team.
         importer = Importer('aardvarks')
         importer.importAddresses((
             'anne.person@xxxxxxxxxxx',

=== modified file 'lib/lp/registry/tests/test_person.py'
--- lib/lp/registry/tests/test_person.py	2010-06-21 21:01:01 +0000
+++ lib/lp/registry/tests/test_person.py	2010-07-14 16:21:15 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2010 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
@@ -17,16 +17,12 @@
 from canonical.database.sqlbase import cursor
 from canonical.launchpad.interfaces.emailaddress import EmailAddressStatus
 from canonical.launchpad.ftests import ANONYMOUS, login
-from lp.soyuz.interfaces.archive import ArchivePurpose, IArchiveSet
-from lp.bugs.interfaces.bug import CreateBugParams, IBugSet
 from canonical.launchpad.interfaces.emailaddress import (
-    EmailAddressAlreadyTaken, IEmailAddressSet, InvalidEmailAddress)
+    EmailAddressAlreadyTaken, InvalidEmailAddress)
 from lazr.lifecycle.snapshot import Snapshot
-from lp.blueprints.interfaces.specification import ISpecificationSet
 from lp.registry.interfaces.karma import IKarmaCacheManager
 from lp.registry.interfaces.person import InvalidName
 from lp.registry.interfaces.product import IProductSet
-from lp.registry.interfaces.mailinglist import IMailingListSet
 from lp.registry.interfaces.person import (
     IPersonSet, ImmutableVisibilityError, NameAlreadyTaken,
     PersonCreationRationale, PersonVisibility)
@@ -41,7 +37,6 @@
 from lp.blueprints.model.specification import Specification
 from lp.testing import login_person, logout, TestCaseWithFactory
 from lp.testing.views import create_initialized_view
-from lp.registry.interfaces.mailinglist import MailingListStatus
 from lp.registry.interfaces.person import PrivatePersonLinkageError
 from canonical.testing.layers import DatabaseFunctionalLayer, reconnect_stores
 
@@ -142,21 +137,6 @@
                 PrivatePersonLinkageError,
                 setattr, specification, attr_name, self.myteam)
 
-    def test_visibility_validator_announcement(self):
-        self.bzr.announce(
-            user = self.otherteam,
-            title = 'title foo',
-            summary = 'summary foo',
-            url = 'http://foo.com',
-            publication_date = self.now)
-        try:
-            self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        except ImmutableVisibilityError, exc:
-            self.assertEqual(
-                str(exc),
-                'This team cannot be converted to Private Membership since '
-                'it is referenced by an announcement.')
-
     def test_visibility_validator_caching(self):
         # The method Person.visibilityConsistencyWarning can be called twice
         # when editing a team.  The first is part of the form validator.  It
@@ -167,217 +147,9 @@
         naked_team = removeSecurityProxy(self.otherteam)
         naked_team._visibility_warning_cache = fake_warning
         warning = self.otherteam.visibilityConsistencyWarning(
-            PersonVisibility.PRIVATE_MEMBERSHIP)
+            PersonVisibility.PRIVATE)
         self.assertEqual(fake_warning, warning)
 
-    def test_visibility_validator_answer_contact(self):
-        AnswerContact(
-            person=self.otherteam,
-            product=self.bzr,
-            distribution=None,
-            sourcepackagename=None)
-        try:
-            self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        except ImmutableVisibilityError, exc:
-            self.assertEqual(
-                str(exc),
-                'This team cannot be converted to Private Membership since '
-                'it is referenced by an answercontact.')
-
-    def test_visibility_validator_archive(self):
-        getUtility(IArchiveSet).new(
-            owner=self.otherteam,
-            description='desc foo',
-            purpose=ArchivePurpose.PPA)
-        try:
-            self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        except ImmutableVisibilityError, exc:
-            self.assertEqual(
-                str(exc),
-                'This team cannot be converted to Private Membership since '
-                'it is referenced by an archive.')
-
-    def test_visibility_validator_branch(self):
-        self.factory.makeProductBranch(
-            registrant=self.otherteam,
-            owner=self.otherteam,
-            product=self.bzr)
-        try:
-            self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        except ImmutableVisibilityError, exc:
-            self.assertEqual(
-                str(exc),
-                'This team cannot be converted to Private Membership since '
-                'it is referenced by a branch and a branchsubscription.')
-
-    def test_visibility_validator_bug(self):
-        bug_params = CreateBugParams(
-            owner=self.otherteam,
-            title='title foo',
-            comment='comment foo',
-            description='description foo',
-            datecreated=self.now)
-        bug_params.setBugTarget(product=self.bzr)
-        bug = getUtility(IBugSet).createBug(bug_params)
-        bug.bugtasks[0].transitionToAssignee(self.otherteam)
-        try:
-            self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        except ImmutableVisibilityError, exc:
-            self.assertEqual(
-                str(exc),
-                'This team cannot be converted to Private Membership since '
-                'it is referenced by a bug, a bugactivity, '
-                'a bugaffectsperson, a bugnotificationrecipient, '
-                'a bugsubscription, a bugtask and a message.')
-
-    def test_visibility_validator_product_subscription(self):
-        self.bzr.addSubscription(
-            self.otherteam, getUtility(IPersonSet).getByName('name16'))
-        try:
-            self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        except ImmutableVisibilityError, exc:
-            self.assertEqual(
-                str(exc),
-                'This team cannot be converted to Private Membership since '
-                'it is referenced by a project subscriber.')
-
-    def test_visibility_validator_specification_subscriber(self):
-        email = getUtility(IEmailAddressSet).new(
-            'otherteam@xxxxxxxxxxxxx', self.otherteam)
-        self.otherteam.setContactAddress(email)
-        specification = getUtility(ISpecificationSet).get(1)
-        specification.subscribe(self.otherteam, self.otherteam, True)
-        try:
-            self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        except ImmutableVisibilityError, exc:
-            self.assertEqual(
-                str(exc),
-                'This team cannot be converted to Private Membership since '
-                'it is referenced by a specificationsubscription.')
-
-    def test_visibility_validator_team_member(self):
-        self.guadamen.addMember(self.otherteam, self.guadamen)
-        try:
-            self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        except ImmutableVisibilityError, exc:
-            self.assertEqual(
-                str(exc),
-                'This team cannot be converted to Private Membership since '
-                'it is referenced by a teammembership.')
-
-    def test_visibility_validator_team_mailinglist_public(self):
-        self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        getUtility(IMailingListSet).new(self.otherteam)
-        try:
-            self.otherteam.visibility = PersonVisibility.PUBLIC
-        except ImmutableVisibilityError, exc:
-            self.assertEqual(
-                str(exc),
-                'This team cannot be made public since it has a mailing list')
-
-    def test_visibility_validator_team_mailinglist_public_view(self):
-        self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        getUtility(IMailingListSet).new(self.otherteam)
-        # The view should add an error notification.
-        view = create_initialized_view(self.otherteam, '+edit', {
-            'field.name': 'otherteam',
-            'field.displayname': 'Other Team',
-            'field.subscriptionpolicy': 'RESTRICTED',
-            'field.renewal_policy': 'NONE',
-            'field.visibility': 'PUBLIC',
-            'field.actions.save': 'Save',
-            })
-        self.assertEqual(len(view.request.notifications), 1)
-        self.assertEqual(
-            view.request.notifications[0].message,
-            'This team cannot be made public since it has a mailing list')
-
-    def test_visibility_validator_team_mailinglist_public_purged(self):
-        self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        mailinglist = getUtility(IMailingListSet).new(self.otherteam)
-        mailinglist.startConstructing()
-        mailinglist.transitionToStatus(MailingListStatus.ACTIVE)
-        mailinglist.deactivate()
-        mailinglist.transitionToStatus(MailingListStatus.INACTIVE)
-        mailinglist.purge()
-        self.otherteam.visibility = PersonVisibility.PUBLIC
-        self.assertEqual(self.otherteam.visibility, PersonVisibility.PUBLIC)
-
-    def test_visibility_validator_team_mailinglist_private(self):
-        getUtility(IMailingListSet).new(self.otherteam)
-        try:
-            self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        except ImmutableVisibilityError, exc:
-            self.assertEqual(
-                str(exc),
-                'This team cannot be converted to Private Membership '
-                'since it is referenced by a mailing list.')
-
-    def test_visibility_validator_team_mailinglist_private_view(self):
-        # The view should add a field error.
-        getUtility(IMailingListSet).new(self.otherteam)
-        view = create_initialized_view(self.otherteam, '+edit', {
-            'field.name': 'otherteam',
-            'field.displayname': 'Other Team',
-            'field.subscriptionpolicy': 'RESTRICTED',
-            'field.renewal_policy': 'NONE',
-            'field.visibility': 'PRIVATE_MEMBERSHIP',
-            'field.actions.save': 'Save',
-            })
-        self.assertEqual(len(view.errors), 1)
-        self.assertEqual(
-            view.errors[0],
-            'This team cannot be converted to Private '
-            'Membership since it is referenced by a mailing list.')
-
-    def test_visibility_validator_team_mailinglist_pmt_to_private(self):
-        # A PRIVATE_MEMBERSHIP team with a mailing list may convert to a
-        # PRIVATE.
-        self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        getUtility(IMailingListSet).new(self.otherteam)
-        self.otherteam.visibility = PersonVisibility.PRIVATE
-
-    def test_visibility_validator_team_mailinglist_pmt_to_private_view(self):
-        # A PRIVATE_MEMBERSHIP team with a mailing list may convert to a
-        # PRIVATE.
-        self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        getUtility(IMailingListSet).new(self.otherteam)
-        view = create_initialized_view(self.otherteam, '+edit', {
-            'field.name': 'otherteam',
-            'field.displayname': 'Other Team',
-            'field.subscriptionpolicy': 'RESTRICTED',
-            'field.renewal_policy': 'NONE',
-            'field.visibility': 'PRIVATE',
-            'field.actions.save': 'Save',
-            })
-        self.assertEqual(len(view.errors), 0)
-
-    def test_visibility_validator_team_private_to_pmt(self):
-        # A PRIVATE team cannot convert to PRIVATE_MEMBERSHIP.
-        self.otherteam.visibility = PersonVisibility.PRIVATE
-        try:
-            self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        except ImmutableVisibilityError, exc:
-            self.assertEqual(
-                str(exc),
-                'A private team cannot change visibility.')
-
-    def test_visibility_validator_team_private_to_pmt_view(self):
-        # A PRIVATE team cannot convert to PRIVATE_MEMBERSHIP.
-        self.otherteam.visibility = PersonVisibility.PRIVATE
-        view = create_initialized_view(self.otherteam, '+edit', {
-            'field.name': 'otherteam',
-            'field.displayname': 'Other Team',
-            'field.subscriptionpolicy': 'RESTRICTED',
-            'field.renewal_policy': 'NONE',
-            'field.visibility': 'PRIVATE_MEMBERSHIP',
-            'field.actions.save': 'Save',
-            })
-        self.assertEqual(len(view.errors), 0)
-        self.assertEqual(len(view.request.notifications), 1)
-        self.assertEqual(view.request.notifications[0].message,
-                         'A private team cannot change visibility.')
-
     def test_visibility_validator_team_ss_prod_pub_to_private(self):
         # A PUBLIC team with a structural subscription to a product can
         # convert to a PRIVATE team.
@@ -413,17 +185,6 @@
         self.assertEqual(view.request.notifications[0].message,
                          'A private team cannot change visibility.')
 
-    def test_visibility_validator_team_mailinglist_private_purged(self):
-        mailinglist = getUtility(IMailingListSet).new(self.otherteam)
-        mailinglist.startConstructing()
-        mailinglist.transitionToStatus(MailingListStatus.ACTIVE)
-        mailinglist.deactivate()
-        mailinglist.transitionToStatus(MailingListStatus.INACTIVE)
-        mailinglist.purge()
-        self.otherteam.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
-        self.assertEqual(self.otherteam.visibility,
-                         PersonVisibility.PRIVATE_MEMBERSHIP)
-
     def test_person_snapshot(self):
         omitted = (
             'activemembers', 'adminmembers', 'allmembers', 'approvedmembers',
@@ -594,7 +355,8 @@
         self.failUnlessEqual(owner, params.owner)
         self.failUnlessEqual(bug_commenter, params.bug_commenter)
         self.failUnlessEqual(bug_reporter, params.bug_reporter)
-        self.failUnlessEqual(structural_subscriber, params.structural_subscriber)
+        self.failUnlessEqual(structural_subscriber,
+                             params.structural_subscriber)
 
     def test_get_related_bugtasks_search_params(self):
         # With no specified options, get_related_bugtasks_search_params()
@@ -628,7 +390,8 @@
         self.checkUserFields(
             search_params[2], assignee=self.user, bug_commenter=self.context)
         self.checkUserFields(
-            search_params[3], assignee=self.user, structural_subscriber=self.context)
+            search_params[3], assignee=self.user,
+            structural_subscriber=self.context)
 
     def test_get_related_bugtasks_search_params_with_owner(self):
         # With owner specified, get_related_bugtasks_search_params() returns
@@ -643,7 +406,8 @@
         self.checkUserFields(
             search_params[2], owner=self.user, bug_commenter=self.context)
         self.checkUserFields(
-            search_params[3], owner=self.user, structural_subscriber=self.context)
+            search_params[3], owner=self.user,
+            structural_subscriber=self.context)
 
     def test_get_related_bugtasks_search_params_with_bug_reporter(self):
         # With bug reporter specified, get_related_bugtasks_search_params()

=== modified file 'lib/lp/registry/tests/test_user_vocabularies.py'
--- lib/lp/registry/tests/test_user_vocabularies.py	2010-05-25 14:50:42 +0000
+++ lib/lp/registry/tests/test_user_vocabularies.py	2010-07-14 16:21:15 +0000
@@ -56,9 +56,9 @@
         login_person(team_owner)
         team = self.factory.makeTeam(owner=team_owner)
         team.addMember(person=user, reviewer=team_owner)
-        # Launchpad admin rights are needed to set private membership.
+        # Launchpad admin rights are needed to set private.
         login('foo.bar@xxxxxxxxxxxxx')
-        team.visibility = PersonVisibility.PRIVATE_MEMBERSHIP
+        team.visibility = PersonVisibility.PRIVATE
         login_person(user)
         self.assertEqual([user], self._vocabTermValues())