← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stevenk/launchpad/refactor-team-rename into lp:launchpad

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/refactor-team-rename into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #853710 in Launchpad itself: "Teams that have ever had a PPA cannot be renamed"
  https://bugs.launchpad.net/launchpad/+bug/853710

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/refactor-team-rename/+merge/78037

Refactor person and team rename checking into common code.

Currently, when renaming a person, if the person has a deleted PPA, or a PPA with no publications, the rename is allowed. If a team has a PPA *at all*, the rename is forbidden. This is somewhat wrong, and leads the bugs like the one linked.

I have also slightly cleaned up the tests related to this.
-- 
https://code.launchpad.net/~stevenk/launchpad/refactor-team-rename/+merge/78037
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/refactor-team-rename into lp:launchpad.
=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py	2011-09-29 10:06:09 +0000
+++ lib/lp/registry/browser/person.py	2011-10-04 04:33:26 +0000
@@ -298,6 +298,7 @@
     Milestone,
     milestone_sort_key,
     )
+from lp.registry.model.person import PersonRenameMixin
 from lp.services.fields import LocationField
 from lp.services.geoip.interfaces import IRequestPreferredLanguages
 from lp.services.messages.interfaces.message import (
@@ -322,8 +323,6 @@
 from lp.soyuz.browser.archivesubscription import (
     traverse_archive_subscription_for_subscriber,
     )
-from lp.soyuz.enums import ArchiveStatus
-from lp.soyuz.interfaces.archive import IArchiveSet
 from lp.soyuz.interfaces.archivesubscriber import IArchiveSubscriberSet
 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
 from lp.soyuz.interfaces.publishing import ISourcePackagePublishingHistory
@@ -4030,7 +4029,7 @@
     page_title = label
 
 
-class PersonEditView(BasePersonEditView):
+class PersonEditView(BasePersonEditView, PersonRenameMixin):
     """The Person 'Edit' page."""
 
     field_names = ['displayname', 'name', 'mugshot', 'homepage_content',
@@ -4050,21 +4049,15 @@
     def setUpWidgets(self):
         """See `LaunchpadViewForm`.
 
-        When a user has a PPA renames are prohibited.
+        When a user has an active PPA renames are prohibited.
         """
-        has_ppa_with_published_packages = (
-            getUtility(IArchiveSet).getPPAOwnedByPerson(
-                self.context, has_packages=True,
-                statuses=[ArchiveStatus.ACTIVE,
-                          ArchiveStatus.DELETING]) is not None)
-        if has_ppa_with_published_packages:
+        reason = super(PersonEditView, self).can_be_renamed()
+        if reason:
             # This makes the field's widget display (i.e. read) only.
             self.form_fields['name'].for_display = True
         super(PersonEditView, self).setUpWidgets()
-        if has_ppa_with_published_packages:
-            self.widgets['name'].hint = _(
-                'This user has an active PPA with packages published and '
-                'may not be renamed.')
+        if reason:
+            self.widgets['name'].hint = reason
 
     def validate(self, data):
         """If the name changed, warn the user about the implications."""

=== modified file 'lib/lp/registry/browser/team.py'
--- lib/lp/registry/browser/team.py	2011-08-23 23:32:26 +0000
+++ lib/lp/registry/browser/team.py	2011-10-04 04:33:26 +0000
@@ -94,6 +94,7 @@
     CyclicalTeamMembershipError,
     TeamMembershipStatus,
     )
+from lp.registry.model.person import PersonRenameMixin
 from lp.services.fields import PublicPersonChoice
 from lp.services.propertycache import cachedproperty
 
@@ -199,7 +200,7 @@
 
 
 class TeamEditView(TeamFormMixin, HasRenewalPolicyMixin,
-                   LaunchpadEditFormView):
+                   LaunchpadEditFormView, PersonRenameMixin):
     """View for editing team details."""
     schema = ITeam
 
@@ -252,17 +253,11 @@
     def setUpWidgets(self):
         """See `LaunchpadViewForm`.
 
-        When a team has a mailing list, a PPA or is private, renames
-        are prohibited.
+        When a team has a mailing list or an active PPA, renames are
+        prohibited.
         """
-        mailing_list = getUtility(IMailingListSet).get(self.context.name)
-        has_mailing_list = (
-            mailing_list is not None and
-            mailing_list.status != MailingListStatus.PURGED)
-        has_ppa = self.context.archive is not None
-
-        block_renaming = (has_mailing_list or has_ppa)
-        if block_renaming:
+        reason = super(TeamEditView, self).can_be_renamed()
+        if reason:
             # This makes the field's widget display (i.e. read) only.
             self.form_fields['name'].for_display = True
 
@@ -270,16 +265,8 @@
 
         # Tweak the field form-help including an explanation for the
         # read-only mode if necessary.
-        if block_renaming:
-            # Group the read-only mode reasons in textual form.
-            reasons = []
-            if has_mailing_list:
-                reasons.append('has a mailing list')
-            if has_ppa:
-                reasons.append('has a PPA')
-            reason = ' and '.join(reasons)
-            self.widgets['name'].hint = _(
-                'This team cannot be renamed because it %s.' % reason)
+        if reason:
+            self.widgets['name'].hint = reason
 
 
 def generateTokenAndValidationEmail(email, team):

=== modified file 'lib/lp/registry/browser/tests/test_team_view.py'
--- lib/lp/registry/browser/tests/test_team_view.py	2011-08-25 01:43:55 +0000
+++ lib/lp/registry/browser/tests/test_team_view.py	2011-10-04 04:33:26 +0000
@@ -1,4 +1,4 @@
-# Copyright 2010 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """
@@ -21,6 +21,7 @@
     PersonVisibility,
     TeamSubscriptionPolicy,
     TeamMembershipRenewalPolicy)
+from lp.soyuz.enums import ArchiveStatus
 from lp.testing import (
     login_person,
     person_logged_in,
@@ -159,17 +160,32 @@
 
     layer = LaunchpadFunctionalLayer
 
-    def test_cannot_rename_team_with_ppa(self):
-        # A team with a ppa cannot be renamed.
+    def test_cannot_rename_team_with_active_ppa(self):
+        # A team with an active PPA that contains publications cannot be
+        # renamed.
         owner = self.factory.makePerson()
         team = self.factory.makeTeam(owner=owner)
-        removeSecurityProxy(team).archive = self.factory.makeArchive()
+        archive = self.factory.makeArchive(owner=team)
+        self.factory.makeSourcePackagePublishingHistory(archive=archive)
+        removeSecurityProxy(team).archive = archive
         with person_logged_in(owner):
             view = create_initialized_view(team, name="+edit")
             self.assertTrue(view.form_fields['name'].for_display)
             self.assertEqual(
-                'This team cannot be renamed because it has a PPA.',
-                view.widgets['name'].hint)
+                'This team has an active PPA with packages published and '
+                'may not be renamed.', view.widgets['name'].hint)
+
+    def test_can_rename_team_with_deleted_ppa(self):
+        # A team with a deleted PPA can be renamed.
+        owner = self.factory.makePerson()
+        team = self.factory.makeTeam(owner=owner)
+        archive = self.factory.makeArchive()
+        self.factory.makeSourcePackagePublishingHistory(archive=archive)
+        removeSecurityProxy(archive).status = ArchiveStatus.DELETED
+        removeSecurityProxy(team).archive = archive
+        with person_logged_in(owner):
+            view = create_initialized_view(team, name="+edit")
+            self.assertFalse(view.form_fields['name'].for_display)
 
     def test_cannot_rename_team_with_active_mailinglist(self):
         # Because renaming mailing lists is non-trivial in Mailman 2.1,
@@ -181,7 +197,7 @@
             view = create_initialized_view(team, name="+edit")
             self.assertTrue(view.form_fields['name'].for_display)
             self.assertEqual(
-                'This team cannot be renamed because it has a mailing list.',
+                'This team has a mailing list and may not be renamed.',
                 view.widgets['name'].hint)
 
     def test_can_rename_team_with_purged_mailinglist(self):
@@ -203,13 +219,15 @@
         owner = self.factory.makePerson()
         team = self.factory.makeTeam(owner=owner)
         self.factory.makeMailingList(team, owner)
-        removeSecurityProxy(team).archive = self.factory.makeArchive()
+        archive = self.factory.makeArchive(owner=team)
+        self.factory.makeSourcePackagePublishingHistory(archive=archive)
+        removeSecurityProxy(team).archive = archive
         with person_logged_in(owner):
             view = create_initialized_view(team, name="+edit")
             self.assertTrue(view.form_fields['name'].for_display)
             self.assertEqual(
-                ('This team cannot be renamed because it has a mailing list '
-                 'and has a PPA.'),
+                'This team has an active PPA with packages published and '
+                'a mailing list and may not be renamed.',
                 view.widgets['name'].hint)
 
     def test_edit_team_view_permission(self):

=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py	2011-09-27 16:14:09 +0000
+++ lib/lp/registry/model/person.py	2011-10-04 04:33:26 +0000
@@ -20,6 +20,7 @@
     'Person',
     'person_sort_key',
     'PersonLanguage',
+    'PersonRenameMixin',
     'PersonSet',
     'SSHKey',
     'SSHKeySet',
@@ -115,6 +116,7 @@
     SQLBase,
     sqlvalues,
     )
+from canonical.launchpad import _
 from canonical.launchpad.components.decoratedresultset import (
     DecoratedResultSet,
     )
@@ -4737,3 +4739,36 @@
         recipient_ids,
         need_validity=True,
         need_preferred_email=True)
+
+
+class PersonRenameMixin:
+    """A mixin class that implements the checks if a person or team can be
+    renamed.
+    """
+
+    def can_be_renamed(self):
+        reasons = []
+        atom = 'user'
+        has_ppa = getUtility(IArchiveSet).getPPAOwnedByPerson(
+            self.context, has_packages=True,
+            statuses=[ArchiveStatus.ACTIVE,
+                      ArchiveStatus.DELETING]) is not None
+        has_mailing_list = None
+        if ITeam.providedBy(self.context):
+            atom = 'team'
+            mailing_list = getUtility(IMailingListSet).get(self.context.name)
+            has_mailing_list = (
+                mailing_list is not None and
+                mailing_list.status != MailingListStatus.PURGED)
+        if has_ppa:
+            reasons.append('has an active PPA with packages published')
+        if has_mailing_list:
+            if has_ppa:
+                reasons.append('a mailing list')
+            else:
+                reasons.append('has a mailing list')
+        if reasons:
+            return _('This %s %s and may not be renamed.' % (
+                atom, ' and '.join(reasons)))
+        else:
+            return None