Merge lp:~james-w/launchpad/suppress-notifications-for-all into lp:launchpad

Proposed by James Westby
Status: Merged
Approved by: Jonathan Lange
Approved revision: no longer in the source branch.
Merged at revision: 15383
Proposed branch: lp:~james-w/launchpad/suppress-notifications-for-all
Merge into: lp:launchpad
Diff against target: 298 lines (+44/-82)
12 files modified
lib/lp/registry/model/person.py (+1/-2)
lib/lp/soyuz/browser/archive.py (+0/-7)
lib/lp/soyuz/configure.zcml (+3/-3)
lib/lp/soyuz/interfaces/archive.py (+1/-1)
lib/lp/soyuz/model/archive.py (+3/-10)
lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt (+2/-6)
lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt (+5/-15)
lib/lp/soyuz/stories/webservice/xx-archive.txt (+12/-0)
lib/lp/soyuz/tests/test_archive.py (+12/-9)
lib/lp/soyuz/tests/test_archive_privacy.py (+0/-11)
lib/lp/soyuz/tests/test_archive_subscriptions.py (+3/-9)
lib/lp/soyuz/tests/test_person_createppa.py (+2/-9)
To merge this branch: bzr merge lp:~james-w/launchpad/suppress-notifications-for-all
Reviewer Review Type Date Requested Status
Benji York (community) code Approve
Review via email: mp+107560@code.launchpad.net

Commit message

Allow the archive owner to set 'suppress_subscription_notifications'

Description of the change

= Summary =

Archives have a "suppress_subscription_notifications" attribute, that for
historical reasons is only settable by commercial admins. There's nothing
sensitive about the attribute, so we can allow it to be set by any owner.

== Proposed fix ==

Change the permissions for the attribute to require Launchpad.Edit rather
than Launchpad.Commercial.

== Pre-implementation notes ==

I didn't have a pre-implementation call with anyone on the LP team.

== LOC Rationale ==

38 lines in credit, bringing us to 140 total for this arc of work.

== Implementation details ==

Changing the permission required is pretty easy, and most of the
other changes are test changes to test the new behaviour rather
than the old.

This change does drop the (partially implemented) restriction that
only private archives can have suppress_subscription_notifications.
I decided this was best as there would be a lot of lines of code
required to enforce this, even leaving aside presenting that
restriction well in the UI.

This change does not make the attribute setable on the +edit page
of the archive, leaving it on +admin for now. I'm not sure that
it should be moved to +edit, but it can be done in a followup
branch if needed.

There are also a couple of changes that seem unrelated, but they
are cleaning up debt in this area (changes to the commercial
archives doctest file so that it no longer refers to a file
that doesn't exist, and doesn't create an archive that isn't
used anywhere else in the file.)

== Tests ==

There are new tests that check that owners can set and change
the attribute, and a tests that a non-owner can't. Tests
were removed that checked that commercial admins can set
the attribute.

== Demo and Q/A ==

I believe that testing that the attribute can still be
toggled on the +admin form should be sufficient for
allowing this to rollout. Checking that owners can now
change the attribute will be necessary for closing the
bug.

I don't think that testing that the attribute still causes
emails to be suppressed is necessary as none of that code
changed.

= Launchpad lint =

I don't think these reported issues should be addressed
as they would harm readability:

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/soyuz/tests/test_archive_subscriptions.py
  lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt
  lib/lp/soyuz/stories/webservice/xx-archive.txt
  lib/lp/soyuz/tests/test_archive.py
  lib/lp/soyuz/interfaces/archive.py
  lib/lp/soyuz/configure.zcml
  lib/lp/soyuz/tests/test_person_createppa.py
  lib/lp/soyuz/tests/test_archive_privacy.py
  lib/lp/soyuz/model/archive.py
  lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt
  lib/lp/registry/model/person.py
  lib/lp/soyuz/browser/archive.py

./lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt
     278: want exceeds 78 characters.
./lib/lp/soyuz/stories/webservice/xx-archive.txt
      42: want exceeds 78 characters.
      46: want exceeds 78 characters.
     169: want exceeds 78 characters.
     185: want exceeds 78 characters.
     201: want exceeds 78 characters.
     217: want exceeds 78 characters.
     361: want exceeds 78 characters.
     422: want exceeds 78 characters.
     553: want exceeds 78 characters.
./lib/lp/soyuz/interfaces/archive.py
     913: E303 too many blank lines (2)
./lib/lp/soyuz/model/archive.py
     346: redefinition of function 'suppress_subscription_notifications' from line 342
     357: redefinition of function 'private' from line 353
./lib/lp/registry/model/person.py
    2990: E501 line too long (84 characters)

To post a comment you must log in.
Revision history for this message
Benji York (benji) wrote :

This branch looks good. I spotted one small improvement you could make
and had a couple of thoughts about lint while reading over it.

The newly-dedented return on line 104 of the diff can now fit on one
line.

Lint:

    - I concur that leaving the long lines for doctests is normally the
      right thing to do.

    - It looks like fixing the "too many blank lines" in archive.py
      wouldn't hurt too much.

    - I couldn't find the long line reported on line 2990 of person.py.
      Maybe the lint report quoted was from a point in time when the
      changed code was all on one line.

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/model/person.py'
2--- lib/lp/registry/model/person.py 2012-05-29 03:59:16 +0000
3+++ lib/lp/registry/model/person.py 2012-06-07 20:40:28 +0000
4@@ -2980,8 +2980,7 @@
5 # the PPA. This is not true in general, and particularly not for
6 # teams. Instead, both the acting user and the target of the PPA
7 # creation ought to be passed through.
8- errors = Archive.validatePPA(
9- self, name, private, suppress_subscription_notifications)
10+ errors = Archive.validatePPA(self, name, private)
11 if errors:
12 raise PPACreationError(errors)
13 ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
14
15=== modified file 'lib/lp/soyuz/browser/archive.py'
16--- lib/lp/soyuz/browser/archive.py 2012-05-04 12:24:11 +0000
17+++ lib/lp/soyuz/browser/archive.py 2012-06-07 20:40:28 +0000
18@@ -2172,13 +2172,6 @@
19 error_text = "\n".join(errors)
20 self.setFieldError('external_dependencies', error_text)
21
22- if (data.get('suppress_subscription_notifications') is True
23- and not data['private']):
24- self.setFieldError(
25- 'suppress_subscription_notifications',
26- 'Can only suppress subscription notifications for private '
27- 'archives.')
28-
29 enabled_restricted_families = data.get('enabled_restricted_families')
30 require_virtualized = data.get('require_virtualized')
31 proc_family_set = getUtility(IProcessorFamilySet)
32
33=== modified file 'lib/lp/soyuz/configure.zcml'
34--- lib/lp/soyuz/configure.zcml 2012-05-21 17:29:19 +0000
35+++ lib/lp/soyuz/configure.zcml 2012-06-07 20:40:28 +0000
36@@ -402,7 +402,8 @@
37 <require
38 permission="launchpad.Edit"
39 interface="lp.soyuz.interfaces.archive.IArchiveEdit"
40- set_attributes="description displayname publish status"/>
41+ set_attributes="description displayname publish status
42+ suppress_subscription_notifications"/>
43 <!--
44 NOTE: The 'private' permission controls who can turn a public
45 archive into a private one, and vice versa. The logic that says this
46@@ -415,8 +416,7 @@
47 set_attributes="authorized_size build_debug_symbols buildd_secret
48 enabled_restricted_families
49 external_dependencies private
50- require_virtualized
51- suppress_subscription_notifications"/>
52+ require_virtualized"/>
53 <require
54 permission="launchpad.Moderate"
55 set_schema="lp.soyuz.interfaces.archive.IArchiveRestricted"/>
56
57=== modified file 'lib/lp/soyuz/interfaces/archive.py'
58--- lib/lp/soyuz/interfaces/archive.py 2012-05-29 15:54:57 +0000
59+++ lib/lp/soyuz/interfaces/archive.py 2012-06-07 20:40:28 +0000
60@@ -325,7 +325,7 @@
61 required=True,
62 description=_(
63 "Whether subscribers to private PPAs get emails about their "
64- "subscriptions.")))
65+ "subscriptions. Has no effect on a public PPA.")))
66
67 def checkArchivePermission(person, component_or_package=None):
68 """Check to see if person is allowed to upload to component.
69
70=== modified file 'lib/lp/soyuz/model/archive.py'
71--- lib/lp/soyuz/model/archive.py 2012-05-29 15:54:57 +0000
72+++ lib/lp/soyuz/model/archive.py 2012-06-07 20:40:28 +0000
73@@ -2023,10 +2023,9 @@
74 self.enabled_restricted_families = restricted
75
76 @classmethod
77- def validatePPA(self, person, proposed_name, private=False,
78- suppress_subscription_notifications=False):
79+ def validatePPA(self, person, proposed_name, private=False):
80 ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
81- if private or suppress_subscription_notifications:
82+ if private:
83 # NOTE: This duplicates the policy in lp/soyuz/configure.zcml
84 # which says that one needs 'launchpad.Commercial' permission to
85 # set 'private', and the logic in `AdminByCommercialTeamOrAdmins`
86@@ -2034,13 +2033,7 @@
87 # permissions.
88 role = IPersonRoles(person)
89 if not (role.in_admin or role.in_commercial_admin):
90- if private:
91- return (
92- '%s is not allowed to make private PPAs' % person.name)
93- if suppress_subscription_notifications:
94- return (
95- '%s is not allowed to make PPAs that suppress '
96- 'subscription notifications' % person.name)
97+ return '%s is not allowed to make private PPAs' % person.name
98 if person.is_team and (
99 person.subscriptionpolicy in OPEN_TEAM_POLICY):
100 return "Open teams cannot have PPAs."
101
102=== modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt'
103--- lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt 2012-05-04 12:24:11 +0000
104+++ lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt 2012-06-07 20:40:28 +0000
105@@ -419,10 +419,7 @@
106 'deb not_a_url' is not a complete and valid sources.list entry
107
108
109-Setting the buildd secret for non-private archives also generates an error.
110-Because the "suppress_subscription_notifications" flag is also currently set,
111-removing privacy will also trigger a validation error because the
112-suppress_subscription_notifications flag can only be set on private archives:
113+Setting the buildd secret for non-private archives also generates an error:
114
115 >>> admin_browser.getControl(
116 ... name="field.external_dependencies").value = ""
117@@ -432,8 +429,7 @@
118
119 >>> for error in get_feedback_messages(admin_browser.contents):
120 ... print error
121- There are 2 errors.
122- Can only suppress subscription notifications for private archives.
123+ There is 1 error.
124 Do not specify for non-private archives
125
126
127
128=== modified file 'lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt'
129--- lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt 2012-05-04 12:02:44 +0000
130+++ lib/lp/soyuz/stories/webservice/xx-archive-commercial.txt 2012-06-07 20:40:28 +0000
131@@ -2,20 +2,10 @@
132 Using the webservice with commercial archives
133 =============================================
134
135-(See also soyuz/stories/webservice/xx-archive.txt and
136-soyuz/doc/archive-commercial.txt)
137-
138-Start by making a commercial PPA that we can fetch via the webservice:
139-
140- >>> login("admin@canonical.com")
141- >>> ppa = factory.makeArchive(private=True, name="cpppa",
142- ... suppress_subscription_notifications=True)
143- >>> import transaction
144- >>> transaction.commit()
145- >>> logout()
146-
147-
148-== Software Center Agent ==
149+(See also soyuz/stories/webservice/xx-archive.txt)
150+
151+Software Center Agent
152+---------------------
153
154 Create the P3A where software_center_agent is an owner.
155
156@@ -26,7 +16,7 @@
157 >>> owner = factory.makePerson()
158 >>> ppa_owner = factory.makeTeam(members=[celebrity, owner])
159 >>> archive = factory.makeArchive(name='commercial', private=True,
160- ... owner=ppa_owner)
161+ ... owner=ppa_owner, suppress_subscription_notifications=True)
162 >>> url = "/~%s/+archive/commercial" % archive.owner.name
163 >>> person = factory.makePerson(name='joe')
164 >>> logout()
165
166=== modified file 'lib/lp/soyuz/stories/webservice/xx-archive.txt'
167--- lib/lp/soyuz/stories/webservice/xx-archive.txt 2012-05-23 05:42:24 +0000
168+++ lib/lp/soyuz/stories/webservice/xx-archive.txt 2012-06-07 20:40:28 +0000
169@@ -1177,6 +1177,18 @@
170 foocomm 1.0-2 in hoary
171 (same version already uploaded and waiting in ACCEPTED queue)
172
173+Suppressing notifications
174+-------------------------
175+
176+The owner of the archive can suppress notifications on subscription
177+changes over the API.
178+
179+ >>> private_archive = cprov_webservice.get(
180+ ... cprov_private_ppa['self_link']).jsonBody()
181+ >>> private_archive['suppress_subscription_notifications'] = True
182+ >>> print modify_archive(cprov_webservice, private_archive)
183+ HTTP/1.1 209 ...
184+ ...
185
186 Archive dependencies
187 ====================
188
189=== modified file 'lib/lp/soyuz/tests/test_archive.py'
190--- lib/lp/soyuz/tests/test_archive.py 2012-05-29 15:55:23 +0000
191+++ lib/lp/soyuz/tests/test_archive.py 2012-06-07 20:40:28 +0000
192@@ -1288,22 +1288,25 @@
193 layer = DatabaseFunctionalLayer
194
195 def test_set_and_get_suppress(self):
196- # Basic set and get of the commercial property. Anyone can read
197- # it and it defaults to False.
198+ # Basic set and get of the suppress_subscription_notifications
199+ # property. Anyone can read it and it defaults to False.
200 archive = self.factory.makeArchive()
201 with person_logged_in(archive.owner):
202 self.assertFalse(archive.suppress_subscription_notifications)
203
204- # The archive owner can't change the value.
205- self.assertRaises(
206- Unauthorized,
207- setattr, archive, 'suppress_subscription_notifications', True)
208-
209- # Commercial admins can change it.
210- with celebrity_logged_in('commercial_admin'):
211+ # The archive owner can change the value.
212 archive.suppress_subscription_notifications = True
213 self.assertTrue(archive.suppress_subscription_notifications)
214
215+ def test_most_users_cant_set_suppress(self):
216+ # Basic set and get of the suppress_subscription_notifications
217+ # property. Anyone can read it and it defaults to False.
218+ archive = self.factory.makeArchive()
219+ with person_logged_in(self.factory.makePerson()):
220+ self.assertFalse(archive.suppress_subscription_notifications)
221+ self.assertRaises(Unauthorized,
222+ setattr, archive, 'suppress_subscription_notifications', True)
223+
224
225 class TestBuildDebugSymbols(TestCaseWithFactory):
226 """Tests relating to the build_debug_symbols flag."""
227
228=== modified file 'lib/lp/soyuz/tests/test_archive_privacy.py'
229--- lib/lp/soyuz/tests/test_archive_privacy.py 2012-05-04 12:02:44 +0000
230+++ lib/lp/soyuz/tests/test_archive_privacy.py 2012-06-07 20:40:28 +0000
231@@ -37,17 +37,6 @@
232 with person_logged_in(subscriber):
233 self.assertEqual(ppa.description, "Foo")
234
235- def test_commercial_security(self):
236- # Commercial private PPAs cannot be accessed by non-subscribers.
237- ppa_name = self.factory.getUniqueString()
238- ppa = self.factory.makeArchive(
239- private=True, suppress_subscription_notifications=True,
240- name=ppa_name)
241- non_subscriber = self.factory.makePerson()
242- with person_logged_in(non_subscriber):
243- self.assertEqual(ppa_name, ppa.name)
244- self.assertRaises(Unauthorized, getattr, ppa, 'description')
245-
246
247 class TestArchivePrivacySwitching(TestCaseWithFactory):
248
249
250=== modified file 'lib/lp/soyuz/tests/test_archive_subscriptions.py'
251--- lib/lp/soyuz/tests/test_archive_subscriptions.py 2012-05-04 11:44:05 +0000
252+++ lib/lp/soyuz/tests/test_archive_subscriptions.py 2012-06-07 20:40:28 +0000
253@@ -9,7 +9,6 @@
254 from lp.services.webapp.publisher import canonical_url
255 from lp.testing import (
256 BrowserTestCase,
257- celebrity_logged_in,
258 login_person,
259 person_logged_in,
260 TestCaseWithFactory,
261@@ -107,14 +106,9 @@
262 notifications[0]['to'])
263
264 def test_new_commercial_subscription_no_email(self):
265- # As per bug 611568, an email is not sent for commercial PPAs.
266- with celebrity_logged_in('commercial_admin'):
267- self.archive.suppress_subscription_notifications = True
268-
269- # Logging in as a celebrity team causes an email to be sent
270- # because a person is added as a member of the team, so this
271- # needs to be cleared out before calling newSubscription().
272- pop_notifications()
273+ # As per bug 611568, an email is not sent for
274+ # suppress_subscription_notifications PPAs.
275+ self.archive.suppress_subscription_notifications = True
276
277 self.archive.newSubscription(
278 self.subscriber, registrant=self.archive.owner)
279
280=== modified file 'lib/lp/soyuz/tests/test_person_createppa.py'
281--- lib/lp/soyuz/tests/test_person_createppa.py 2012-05-04 12:13:17 +0000
282+++ lib/lp/soyuz/tests/test_person_createppa.py 2012-06-07 20:40:28 +0000
283@@ -36,13 +36,6 @@
284 PPACreationError, person.createPPA, private=True)
285
286 def test_suppress_subscription_notifications(self):
287- with celebrity_logged_in('commercial_admin') as person:
288- ppa = person.createPPA(suppress_subscription_notifications=True)
289- self.assertEqual(True, ppa.suppress_subscription_notifications)
290-
291- def test_suppress_without_permission(self):
292 person = self.factory.makePerson()
293- with person_logged_in(person):
294- self.assertRaises(
295- PPACreationError, person.createPPA,
296- suppress_subscription_notifications=True)
297+ ppa = person.createPPA(suppress_subscription_notifications=True)
298+ self.assertEqual(True, ppa.suppress_subscription_notifications)