Merge lp:~jcsackett/launchpad/merge-ppas-697685 into lp:launchpad

Proposed by j.c.sackett
Status: Merged
Approved by: j.c.sackett
Approved revision: no longer in the source branch.
Merged at revision: 12195
Proposed branch: lp:~jcsackett/launchpad/merge-ppas-697685
Merge into: lp:launchpad
Diff against target: 183 lines (+106/-53)
2 files modified
lib/lp/registry/browser/peoplemerge.py (+77/-53)
lib/lp/registry/browser/tests/test_peoplemerge.py (+29/-0)
To merge this branch: bzr merge lp:~jcsackett/launchpad/merge-ppas-697685
Reviewer Review Type Date Requested Status
Abel Deuring (community) code Approve
Review via email: mp+45450@code.launchpad.net

Commit message

[r=adeuring][ui=none][bug=697685] Adds validation to RequestPeopleMergeView so that persons with PPAs cannot be merged.

Description of the change

Summary
=======

For a merge to happen, the duplicate entity (team or person) being merged can't have PPAs. This was already prevented from happening in the AdminMerge views, but not in the RequestPeopleMergeView, which is generally used by a person trying to merge another person into their account. The RequestPeopleMergeView needs to have the same validation checks as the AdminMerge views.

Proposed Fix
============

Add the validation step used in AdminMerge views to the other view.

Preimplementation Talk
======================

Spoke with Curtis Hovey about the validation needs.

Implementation
==============

lib/lp/registry/browser/peoplemerge.py
--------------------------------------

A new class, ValidatingMergeView was created that contains the validation step used in AdminMergeBaseView. AdminMergeBaseView and RequestPeopleMergeView both now inherit from this, instead of directly from LaunchpadFormView.

Additionally, the step in validation checking for PPAs now first checks that the dupe person is not None; if the dupe is None, the error is handled by the vocabularies governing merges, so this is safe to do, and prevents an error when the validator tries to find PPAs for NoneType.

Lastly, RequestPeopleMergeView has been grouped with the other RequestPeople merge views, so it's clear in a read through that there are related classes.

lib/lp/registry/browser/tests/test_peoplemerge.py
-------------------------------------------------

Create a new test case to test that duplicate people with ppas cause an error on the RequestPeopleMergeView.

Tests
=====

bin/test -t peoplemerge

Demo & QA
=========

Attempt to merge a person with a PPA into the account you're logged into through +requestmerge. It should provide an error saying you need to delete the PPA.

Obviously, do this on qastaging or launchpad.dev :-P

Lint
====

make lint output:

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/browser/peoplemerge.py
  lib/lp/registry/browser/tests/test_peoplemerge.py

To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/peoplemerge.py'
2--- lib/lp/registry/browser/peoplemerge.py 2011-01-05 01:19:24 +0000
3+++ lib/lp/registry/browser/peoplemerge.py 2011-01-13 15:51:12 +0000
4@@ -52,59 +52,31 @@
5 from lp.soyuz.interfaces.archive import IArchiveSet
6
7
8-class RequestPeopleMergeView(LaunchpadFormView):
9- """The view for the page where the user asks a merge of two accounts.
10-
11- If the dupe account have only one email address we send a message to that
12- address and then redirect the user to other page saying that everything
13- went fine. Otherwise we redirect the user to another page where we list
14- all email addresses owned by the dupe account and the user selects which
15- of those (s)he wants to claim.
16- """
17-
18- label = 'Merge Launchpad accounts'
19- page_title = label
20- schema = IRequestPeopleMerge
21-
22- @property
23- def cancel_url(self):
24- return canonical_url(getUtility(IPersonSet))
25-
26- @action('Continue', name='continue')
27- def continue_action(self, action, data):
28- dupeaccount = data['dupe_person']
29- if dupeaccount == self.user:
30- # Please, don't try to merge you into yourself.
31- return
32-
33- emails = getUtility(IEmailAddressSet).getByPerson(dupeaccount)
34- emails_count = emails.count()
35- if emails_count > 1:
36- # The dupe account have more than one email address. Must redirect
37- # the user to another page to ask which of those emails (s)he
38- # wants to claim.
39- self.next_url = '+requestmerge-multiple?dupe=%d' % dupeaccount.id
40- return
41-
42- assert emails_count == 1
43- email = emails[0]
44- login = getUtility(ILaunchBag).login
45- logintokenset = getUtility(ILoginTokenSet)
46- # Need to remove the security proxy because the dupe account may have
47- # hidden email addresses.
48- token = logintokenset.new(
49- self.user, login, removeSecurityProxy(email).email,
50- LoginTokenType.ACCOUNTMERGE)
51-
52- # XXX: SteveAlexander 2006-03-07: An experiment to see if this
53- # improves problems with merge people tests.
54- import canonical.database.sqlbase
55- canonical.database.sqlbase.flush_database_updates()
56- token.sendMergeRequestEmail()
57- self.next_url = './+mergerequest-sent?dupe=%d' % dupeaccount.id
58-
59-
60-class AdminMergeBaseView(LaunchpadFormView):
61+class ValidatingMergeView(LaunchpadFormView):
62+
63+ def validate(self, data):
64+ """Check that user is not attempting to merge a person into itself."""
65+ dupe_person = data.get('dupe_person')
66+ target_person = data.get('target_person')
67+
68+ if dupe_person == target_person and dupe_person is not None:
69+ self.addError(_("You can't merge ${name} into itself.",
70+ mapping=dict(name=dupe_person.name)))
71+ # We cannot merge if there is a PPA with published
72+ # packages on the duplicate, unless that PPA is removed.
73+ if dupe_person is not None:
74+ dupe_person_ppas = getUtility(IArchiveSet).getPPAOwnedByPerson(
75+ dupe_person, statuses=[ArchiveStatus.ACTIVE,
76+ ArchiveStatus.DELETING])
77+ if dupe_person_ppas is not None:
78+ self.addError(_(
79+ "${name} has a PPA that must be deleted before it "
80+ "can be merged. It may take ten minutes to remove the "
81+ "deleted PPA's files.",
82+ mapping=dict(name=dupe_person.name)))
83+
84+
85+class AdminMergeBaseView(ValidatingMergeView):
86 """Base view for the pages where admins can merge people/teams."""
87
88 page_title = 'Merge Launchpad accounts'
89@@ -507,3 +479,55 @@
90 def email_hidden(self):
91 """Does the duplicate account hide email addresses?"""
92 return self.dupe.hide_email_addresses
93+
94+
95+class RequestPeopleMergeView(ValidatingMergeView):
96+ """The view for the page where the user asks a merge of two accounts.
97+
98+ If the dupe account have only one email address we send a message to that
99+ address and then redirect the user to other page saying that everything
100+ went fine. Otherwise we redirect the user to another page where we list
101+ all email addresses owned by the dupe account and the user selects which
102+ of those (s)he wants to claim.
103+ """
104+
105+ label = 'Merge Launchpad accounts'
106+ page_title = label
107+ schema = IRequestPeopleMerge
108+
109+ @property
110+ def cancel_url(self):
111+ return canonical_url(getUtility(IPersonSet))
112+
113+ @action('Continue', name='continue')
114+ def continue_action(self, action, data):
115+ dupeaccount = data['dupe_person']
116+ if dupeaccount == self.user:
117+ # Please, don't try to merge you into yourself.
118+ return
119+
120+ emails = getUtility(IEmailAddressSet).getByPerson(dupeaccount)
121+ emails_count = emails.count()
122+ if emails_count > 1:
123+ # The dupe account have more than one email address. Must redirect
124+ # the user to another page to ask which of those emails (s)he
125+ # wants to claim.
126+ self.next_url = '+requestmerge-multiple?dupe=%d' % dupeaccount.id
127+ return
128+
129+ assert emails_count == 1
130+ email = emails[0]
131+ login = getUtility(ILaunchBag).login
132+ logintokenset = getUtility(ILoginTokenSet)
133+ # Need to remove the security proxy because the dupe account may have
134+ # hidden email addresses.
135+ token = logintokenset.new(
136+ self.user, login, removeSecurityProxy(email).email,
137+ LoginTokenType.ACCOUNTMERGE)
138+
139+ # XXX: SteveAlexander 2006-03-07: An experiment to see if this
140+ # improves problems with merge people tests.
141+ import canonical.database.sqlbase
142+ canonical.database.sqlbase.flush_database_updates()
143+ token.sendMergeRequestEmail()
144+ self.next_url = './+mergerequest-sent?dupe=%d' % dupeaccount.id
145
146=== modified file 'lib/lp/registry/browser/tests/test_peoplemerge.py'
147--- lib/lp/registry/browser/tests/test_peoplemerge.py 2010-12-21 19:42:00 +0000
148+++ lib/lp/registry/browser/tests/test_peoplemerge.py 2011-01-13 15:51:12 +0000
149@@ -30,6 +30,35 @@
150 )
151
152
153+class TestRequestPeopleMergeView(TestCaseWithFactory):
154+
155+ layer = DatabaseFunctionalLayer
156+
157+ def setUp(self):
158+ super(TestRequestPeopleMergeView, self).setUp()
159+ self.person_set = getUtility(IPersonSet)
160+ self.dupe = self.factory.makePerson(name='dupe')
161+ self.target = self.factory.makeTeam(name='target')
162+
163+ def test_cannot_merge_person_with_ppas(self):
164+ # A team with a PPA cannot be merged.
165+ login_celebrity('admin')
166+ archive = self.dupe.createPPA()
167+ login_celebrity('registry_experts')
168+ form = {
169+ 'field.dupe_person': self.dupe.name,
170+ 'field.target_person': self.target.name,
171+ 'field.actions.continue': 'Continue',
172+ }
173+ view = create_initialized_view(
174+ self.person_set, '+requestmerge', form=form)
175+ self.assertEqual(
176+ [u"dupe has a PPA that must be deleted before it can be "
177+ "merged. It may take ten minutes to remove the deleted PPA's "
178+ "files."],
179+ view.errors)
180+
181+
182 class TestRequestPeopleMergeMultipleEmailsView(TestCaseWithFactory):
183 """Test the RequestPeopleMergeMultipleEmailsView rules."""
184