Merge lp:~brian-murray/launchpad/bug-supervisor-nominate-for-release into lp:launchpad
- bug-supervisor-nominate-for-release
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Aaron Bentley |
Approved revision: | no longer in the source branch. |
Merged at revision: | 11774 |
Proposed branch: | lp:~brian-murray/launchpad/bug-supervisor-nominate-for-release |
Merge into: | lp:launchpad |
Diff against target: |
839 lines (+351/-124) 17 files modified
lib/canonical/launchpad/interfaces/ftests/validation.txt (+6/-2) lib/canonical/launchpad/security.py (+24/-0) lib/lp/bugs/browser/bug.py (+6/-3) lib/lp/bugs/browser/tests/bug-nomination-views.txt (+22/-12) lib/lp/bugs/browser/tests/bug-views.txt (+20/-6) lib/lp/bugs/configure.zcml (+20/-20) lib/lp/bugs/doc/bug-nomination.txt (+27/-14) lib/lp/bugs/model/bug.py (+7/-0) lib/lp/bugs/stories/bug-release-management/30-nominate-bug-for-distrorelease.txt (+45/-29) lib/lp/bugs/stories/bug-release-management/40-nominate-bug-for-productseries.txt (+46/-33) lib/lp/bugs/stories/bug-release-management/60-defer-product-bug.txt (+7/-1) lib/lp/bugs/stories/bug-release-management/xx-anonymous-bug-nomination.txt (+1/-2) lib/lp/bugs/stories/webservice/xx-bug.txt (+6/-0) lib/lp/bugs/tests/bugs-emailinterface.txt (+23/-0) lib/lp/bugs/tests/test_bugsupervisor_bugnomination.py (+85/-0) lib/lp/registry/interfaces/distribution.py (+1/-1) lib/lp/testing/factory.py (+5/-1) |
To merge this branch: | bzr merge lp:~brian-murray/launchpad/bug-supervisor-nominate-for-release |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Aaron Bentley (community) | Approve | ||
Review via email: mp+38733@code.launchpad.net |
Commit message
Restrict the ability to nominate bugs for a release to bug supervisors, owners or drivers.
Description of the change
Bug 114766 is about making bug nominations more useful, for nomination reviewers, by restricting the number ability to nominate bugs for a release from anyone to the bug supervisor group. This is also something that the Ubuntu community is looking for as noted in bug 174375. I've setup the branch so the that only projects or distributions that have a bug supervisor set are affected by this change.
lib/lp/
restrict the enabling of the +nominate link to the bug supervisor team if set
lib/lp/
added check to ensure that nominator is a member of the correct team
lib/lp/
sorted permission attributes alphabetically
lib/lp/
add in a bug supervisor parameter to makeDistribution (one already exists for makeProject)
lib/lp/
modified tests 30- and 40- not to use sample data
60- added in a nomination that used to be in 30- or 40-
I added in test_bugsupervi
bin/test -cvvt test_bugsupervi
I've also modified the tests in bug-release-
bin/test -cvvt bug-release-
Brian Murray (brian-murray) wrote : | # |
I've updated the branch per our discussions so that only bug supervisors, drivers or owners see the +nominate link and can nominate a bug for a release. I also created 2 more permission classes NominateBugForP
Aaron Bentley (abentley) : | # |
Preview Diff
1 | === modified file 'lib/canonical/launchpad/interfaces/ftests/validation.txt' |
2 | --- lib/canonical/launchpad/interfaces/ftests/validation.txt 2010-10-18 22:24:59 +0000 |
3 | +++ lib/canonical/launchpad/interfaces/ftests/validation.txt 2010-10-21 01:54:46 +0000 |
4 | @@ -1,10 +1,16 @@ |
5 | = Launchpad field validators = |
6 | |
7 | + >>> from zope.security.proxy import removeSecurityProxy |
8 | >>> from zope.component import getUtility |
9 | >>> from canonical.launchpad.webapp.interfaces import IOpenLaunchBag |
10 | >>> from lp.bugs.interfaces.bug import CreateBugParams |
11 | >>> from lp.registry.interfaces.person import IPersonSet |
12 | >>> from lp.registry.interfaces.product import IProductSet |
13 | + >>> no_priv = getUtility(IPersonSet).getByEmail( |
14 | + ... 'no-priv@canonical.com') |
15 | + >>> firefox = getUtility(IProductSet).getByName("firefox") |
16 | + >>> firefox = removeSecurityProxy(firefox) |
17 | + >>> firefox.bug_supervisor = no_priv |
18 | |
19 | == can_be_nominated_for_series == |
20 | |
21 | @@ -16,8 +22,6 @@ |
22 | If we create a new bug, all the target's series can be nominated. |
23 | |
24 | >>> login('no-priv@canonical.com') |
25 | - >>> no_priv = getUtility(IPersonSet).getByEmail( |
26 | - ... 'no-priv@canonical.com') |
27 | >>> firefox = getUtility(IProductSet).getByName('firefox') |
28 | >>> bug = firefox.createBug( |
29 | ... CreateBugParams(no_priv, "New Bug", comment="New Bug.")) |
30 | |
31 | === modified file 'lib/canonical/launchpad/security.py' |
32 | --- lib/canonical/launchpad/security.py 2010-10-14 21:47:33 +0000 |
33 | +++ lib/canonical/launchpad/security.py 2010-10-21 01:54:46 +0000 |
34 | @@ -913,6 +913,30 @@ |
35 | user.in_admin) |
36 | |
37 | |
38 | +class NominateBugForProductSeries(AuthorizationBase): |
39 | + """Product's owners and bug supervisors can add bug nominations.""" |
40 | + |
41 | + permission = 'launchpad.BugSupervisor' |
42 | + usedfor = IProductSeries |
43 | + |
44 | + def checkAuthenticated(self, user): |
45 | + return (user.inTeam(self.obj.product.bug_supervisor) or |
46 | + user.inTeam(self.obj.product.owner) or |
47 | + user.in_admin) |
48 | + |
49 | + |
50 | +class NominateBugForDistroSeries(AuthorizationBase): |
51 | + """Distro's owners and bug supervisors can add bug nominations.""" |
52 | + |
53 | + permission = 'launchpad.BugSupervisor' |
54 | + usedfor = IDistroSeries |
55 | + |
56 | + def checkAuthenticated(self, user): |
57 | + return (user.inTeam(self.obj.distribution.bug_supervisor) or |
58 | + user.inTeam(self.obj.distribution.owner) or |
59 | + user.in_admin) |
60 | + |
61 | + |
62 | class AdminDistroSeries(AdminByAdminsTeam): |
63 | """Soyuz involves huge chunks of data in the archive and librarian, |
64 | so for the moment we are locking down admin and edit on distributions |
65 | |
66 | === modified file 'lib/lp/bugs/browser/bug.py' |
67 | --- lib/lp/bugs/browser/bug.py 2010-09-23 10:27:11 +0000 |
68 | +++ lib/lp/bugs/browser/bug.py 2010-10-21 01:54:46 +0000 |
69 | @@ -273,10 +273,13 @@ |
70 | target = launchbag.product or launchbag.distribution |
71 | if check_permission("launchpad.Driver", target): |
72 | text = "Target to release" |
73 | - else: |
74 | + return Link('+nominate', text, icon='milestone') |
75 | + elif (check_permission("launchpad.BugSupervisor", target) or |
76 | + self.user is None): |
77 | text = 'Nominate for release' |
78 | - |
79 | - return Link('+nominate', text, icon='milestone') |
80 | + return Link('+nominate', text, icon='milestone') |
81 | + else: |
82 | + return Link('+nominate', '', enabled=False, icon='milestone') |
83 | |
84 | def addcomment(self): |
85 | """Return the 'Comment or attach file' Link.""" |
86 | |
87 | === modified file 'lib/lp/bugs/browser/tests/bug-nomination-views.txt' |
88 | --- lib/lp/bugs/browser/tests/bug-nomination-views.txt 2010-10-18 22:24:59 +0000 |
89 | +++ lib/lp/bugs/browser/tests/bug-nomination-views.txt 2010-10-21 01:54:46 +0000 |
90 | @@ -1,18 +1,33 @@ |
91 | = Bug Nomination Pages = |
92 | |
93 | Series targeting is done on the +nominate page of a bug. From here, |
94 | -normal users can propose that the bug be fixed in specific distribution |
95 | +bug supervisors can propose that the bug be fixed in specific distribution |
96 | and product series, and release managers can directly target |
97 | the bug to series for which they are drivers. |
98 | |
99 | + >>> from zope.security.proxy import removeSecurityProxy |
100 | >>> from zope.component import getUtility, getMultiAdapter |
101 | |
102 | + >>> from canonical.launchpad.ftests import login_person |
103 | >>> from canonical.launchpad.webapp.interfaces import IOpenLaunchBag |
104 | >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
105 | >>> from lp.bugs.interfaces.bugtask import IBugTaskSet |
106 | - |
107 | - >>> login("no-priv@canonical.com") |
108 | - |
109 | + >>> from lp.registry.interfaces.distribution import IDistributionSet |
110 | + >>> from lp.registry.interfaces.person import IPersonSet |
111 | + >>> from lp.registry.interfaces.product import IProductSet |
112 | + >>> from lp.testing.sampledata import (ADMIN_EMAIL) |
113 | + |
114 | + >>> login(ADMIN_EMAIL) |
115 | + >>> nominator = factory.makePerson(name='nominator') |
116 | + >>> ubuntu = getUtility(IDistributionSet).getByName("ubuntu") |
117 | + >>> ubuntu = removeSecurityProxy(ubuntu) |
118 | + >>> ubuntu.bug_supervisor = nominator |
119 | + >>> firefox = getUtility(IProductSet).getByName("firefox") |
120 | + >>> firefox = removeSecurityProxy(firefox) |
121 | + >>> firefox.bug_supervisor = nominator |
122 | + >>> logout() |
123 | + |
124 | + >>> login_person(nominator) |
125 | >>> request = LaunchpadTestRequest() |
126 | >>> bug_one_in_ubuntu_firefox = getUtility(IBugTaskSet).get(17) |
127 | >>> print bug_one_in_ubuntu_firefox.bug.id |
128 | @@ -32,7 +47,7 @@ |
129 | |
130 | Here's an example of nominating a bug for a distroseries. |
131 | |
132 | - >>> login("no-priv@canonical.com") |
133 | + >>> login_person(nominator) |
134 | |
135 | >>> request = LaunchpadTestRequest( |
136 | ... method="POST", |
137 | @@ -44,13 +59,10 @@ |
138 | |
139 | (Add objects to the LaunchBag that will be used by the view.) |
140 | |
141 | - >>> from lp.registry.interfaces.distribution import IDistributionSet |
142 | - |
143 | >>> launchbag.clear() |
144 | >>> launchbag.add(bug_one_in_ubuntu_firefox) |
145 | >>> launchbag.add(bug_one_in_ubuntu_firefox.distribution) |
146 | |
147 | - >>> ubuntu = getUtility(IDistributionSet).getByName("ubuntu") |
148 | >>> ubuntu_warty = ubuntu.getSeries("warty") |
149 | |
150 | >>> bug_one = bug_one_in_ubuntu_firefox.bug |
151 | @@ -167,11 +179,11 @@ |
152 | |
153 | The batch navigator used returns a BugTaskListingItem for each of the |
154 | distribution's bugtasks that are nominated for this release, with a |
155 | -widget set up for accepting/declining the nomination. The widet is only |
156 | +widget set up for accepting/declining the nomination. The widget is only |
157 | set up if the user has permission to approve the nomination. |
158 | |
159 | >>> from lp.bugs.interfaces.bug import CreateBugParams |
160 | - >>> login("no-priv@canonical.com") |
161 | + >>> login_person(nominator) |
162 | >>> no_priv = getUtility(ILaunchBag).user |
163 | >>> ubuntu_bug = ubuntu.createBug(CreateBugParams( |
164 | ... no_priv, "Ubuntu bug", comment="Test bug.")) |
165 | @@ -228,8 +240,6 @@ |
166 | context, we mean a user that has, either directly or through a team, |
167 | launchpad.Driver permission on the nomination. |
168 | |
169 | - >>> from lp.registry.interfaces.person import IPersonSet |
170 | - |
171 | >>> ubuntu_team = getUtility(IPersonSet).getByName("ubuntu-team") |
172 | |
173 | >>> login("celso.providelo@canonical.com") |
174 | |
175 | === modified file 'lib/lp/bugs/browser/tests/bug-views.txt' |
176 | --- lib/lp/bugs/browser/tests/bug-views.txt 2010-10-19 18:44:31 +0000 |
177 | +++ lib/lp/bugs/browser/tests/bug-views.txt 2010-10-21 01:54:46 +0000 |
178 | @@ -482,12 +482,26 @@ |
179 | bugtask, New, mozilla-firefox (Ubuntu) |
180 | nomination, Nominated, Ubuntu Hoary |
181 | |
182 | -Let's nominate the bug for upstream and an Ubuntu series and see how |
183 | -the list changes. |
184 | - |
185 | -(Login as an unprivileged user to be able to nominate.) |
186 | - |
187 | - >>> login("no-priv@canonical.com") |
188 | +After creating bug supervisors for Ubuntu and Firefox Let's nominate the bug |
189 | +for upstream and an Ubuntu series and see how the list changes. |
190 | + |
191 | + >>> from lp.testing.sampledata import (ADMIN_EMAIL) |
192 | + >>> from zope.component import getUtility |
193 | + >>> from zope.security.proxy import removeSecurityProxy |
194 | + >>> |
195 | + >>> login(ADMIN_EMAIL) |
196 | + >>> nominator = factory.makePerson(name='nominator') |
197 | + >>> ubuntu = getUtility(IDistributionSet).getByName("ubuntu") |
198 | + >>> ubuntu = removeSecurityProxy(ubuntu) |
199 | + >>> ubuntu.bug_supervisor = nominator |
200 | + >>> firefox = getUtility(IProductSet).getByName("firefox") |
201 | + >>> firefox = removeSecurityProxy(firefox) |
202 | + >>> firefox.bug_supervisor = nominator |
203 | + >>> logout() |
204 | + |
205 | +(Login as a bug supervisor to be able to nominate.) |
206 | + |
207 | + >>> login_person(nominator) |
208 | |
209 | >>> current_user = getUtility(ILaunchBag).user |
210 | >>> ubuntu_warty = ubuntu.getSeries("warty") |
211 | |
212 | === modified file 'lib/lp/bugs/configure.zcml' |
213 | --- lib/lp/bugs/configure.zcml 2010-10-18 01:06:18 +0000 |
214 | +++ lib/lp/bugs/configure.zcml 2010-10-21 01:54:46 +0000 |
215 | @@ -731,35 +731,35 @@ |
216 | <require |
217 | permission="launchpad.Edit" |
218 | attributes=" |
219 | + addAttachment |
220 | + addChange |
221 | addChangeNotification |
222 | addCommentNotification |
223 | + addNomination |
224 | + addTask |
225 | addWatch |
226 | - removeWatch |
227 | + convertToQuestion |
228 | + expireNotifications |
229 | + findCvesInText |
230 | + linkAttachment |
231 | + linkBranch |
232 | linkCVE |
233 | linkCVEAndReturnNothing |
234 | - unlinkCVE |
235 | - findCvesInText |
236 | + linkHWSubmission |
237 | + linkMessage |
238 | + markAsDuplicate |
239 | + markUserAffected |
240 | newMessage |
241 | - linkMessage |
242 | - addAttachment |
243 | - unsubscribeFromDupes |
244 | - subscribe |
245 | - unsubscribe |
246 | - addNomination |
247 | - expireNotifications |
248 | - setStatus |
249 | + removeWatch |
250 | setPrivate |
251 | setSecurityRelated |
252 | - convertToQuestion |
253 | - markUserAffected |
254 | - addTask |
255 | - addChange |
256 | - markAsDuplicate |
257 | - linkHWSubmission |
258 | + setStatus |
259 | + subscribe |
260 | + unlinkBranch |
261 | + unlinkCVE |
262 | unlinkHWSubmission |
263 | - linkBranch |
264 | - unlinkBranch |
265 | - linkAttachment |
266 | + unsubscribe |
267 | + unsubscribeFromDupes |
268 | updateHeat" |
269 | set_attributes=" |
270 | activity initial_message |
271 | |
272 | === modified file 'lib/lp/bugs/doc/bug-nomination.txt' |
273 | --- lib/lp/bugs/doc/bug-nomination.txt 2010-10-18 22:24:59 +0000 |
274 | +++ lib/lp/bugs/doc/bug-nomination.txt 2010-10-21 01:54:46 +0000 |
275 | @@ -1,18 +1,31 @@ |
276 | = Bug Nomination = |
277 | |
278 | -Any logged-in user can nominate a bug to be fixed in a specific |
279 | +A bug supervisor can nominate a bug to be fixed in a specific |
280 | distribution or product series. Nominations are created by |
281 | calling IBug.addNomination. |
282 | |
283 | >>> from zope.component import getUtility |
284 | >>> from zope.interface.verify import verifyClass |
285 | + >>> from zope.security.proxy import removeSecurityProxy |
286 | + >>> from canonical.launchpad.ftests import login_person |
287 | >>> from lp.bugs.interfaces.bug import IBugSet |
288 | >>> from lp.bugs.interfaces.bugnomination import IBugNomination |
289 | >>> from lp.bugs.model.bugnomination import BugNomination |
290 | >>> from lp.registry.interfaces.distribution import IDistributionSet |
291 | >>> from lp.registry.interfaces.person import IPersonSet |
292 | + >>> from lp.registry.interfaces.product import IProductSet |
293 | + >>> from lp.testing.sampledata import (ADMIN_EMAIL) |
294 | + >>> login(ADMIN_EMAIL) |
295 | + >>> nominator = factory.makePerson(name='nominator') |
296 | + >>> ubuntu = getUtility(IDistributionSet).getByName("ubuntu") |
297 | + >>> ubuntu = removeSecurityProxy(ubuntu) |
298 | + >>> ubuntu.bug_supervisor = nominator |
299 | + >>> firefox = getUtility(IProductSet).getByName("firefox") |
300 | + >>> firefox = removeSecurityProxy(firefox) |
301 | + >>> firefox.bug_supervisor = nominator |
302 | + >>> logout() |
303 | |
304 | - >>> login("no-priv@canonical.com") |
305 | + >>> login_person(nominator) |
306 | |
307 | The BugNomination class implements IBugNomination. |
308 | |
309 | @@ -22,19 +35,18 @@ |
310 | >>> bugset = getUtility(IBugSet) |
311 | >>> bug_one = bugset.get(1) |
312 | |
313 | - >>> ubuntu = getUtility(IDistributionSet).getByName("ubuntu") |
314 | >>> ubuntu_grumpy = ubuntu.getSeries("grumpy") |
315 | >>> personset = getUtility(IPersonSet) |
316 | - >>> no_privs = personset.getByName("no-priv") |
317 | + >>> nominator = personset.getByName("nominator") |
318 | |
319 | >>> grumpy_nomination = bug_one.addNomination( |
320 | - ... target=ubuntu_grumpy, owner=no_privs) |
321 | + ... target=ubuntu_grumpy, owner=nominator) |
322 | |
323 | The nomination records the distro series or series for which the bug |
324 | -was nominated and the user that submitted the nomination (the "owner".) |
325 | +was nominated and the user that submitted the nomination (the "owner"). |
326 | |
327 | >>> print grumpy_nomination.owner.name |
328 | - no-priv |
329 | + nominator |
330 | |
331 | >>> print grumpy_nomination.distroseries.fullseriesname |
332 | Ubuntu Grumpy |
333 | @@ -47,13 +59,13 @@ |
334 | |
335 | >>> firefox_trunk = firefox.getSeries("trunk") |
336 | |
337 | - >>> no_privs = personset.getByName("no-priv") |
338 | + >>> nominator = personset.getByName("nominator") |
339 | |
340 | >>> firefox_ms_nomination = bug_one.addNomination( |
341 | - ... target=firefox_trunk, owner=no_privs) |
342 | + ... target=firefox_trunk, owner=nominator) |
343 | |
344 | >>> print firefox_ms_nomination.owner.name |
345 | - no-priv |
346 | + nominator |
347 | |
348 | >>> print firefox_ms_nomination.productseries.title |
349 | Mozilla Firefox trunk series |
350 | @@ -149,7 +161,7 @@ |
351 | as "Nominated". |
352 | |
353 | >>> ubuntu_breezy_autotest_nomination = bug_one.addNomination( |
354 | - ... target=ubuntu_breezy_autotest, owner=no_privs) |
355 | + ... target=ubuntu_breezy_autotest, owner=nominator) |
356 | |
357 | >>> print ubuntu_breezy_autotest_nomination.status.title |
358 | Nominated |
359 | @@ -179,17 +191,17 @@ |
360 | |
361 | >>> current_user = getUtility(ILaunchBag).user |
362 | |
363 | - >>> current_user == no_privs |
364 | + >>> current_user == nominator |
365 | True |
366 | >>> check_permission("launchpad.Driver", firefox_ms_nomination) |
367 | False |
368 | |
369 | - >>> firefox_ms_nomination.approve(no_privs) |
370 | + >>> firefox_ms_nomination.approve(nominator) |
371 | Traceback (most recent call last): |
372 | .. |
373 | Unauthorized: ... |
374 | |
375 | - >>> firefox_ms_nomination.decline(no_privs) |
376 | + >>> firefox_ms_nomination.decline(nominator) |
377 | Traceback (most recent call last): |
378 | .. |
379 | Unauthorized: ... |
380 | @@ -198,6 +210,7 @@ |
381 | |
382 | >>> login("foo.bar@canonical.com") |
383 | |
384 | + >>> no_privs = personset.getByName("no-priv") |
385 | >>> firefox_ms_nomination.target.driver = no_privs |
386 | |
387 | >>> login("no-priv@canonical.com") |
388 | |
389 | === modified file 'lib/lp/bugs/model/bug.py' |
390 | --- lib/lp/bugs/model/bug.py 2010-10-15 16:09:18 +0000 |
391 | +++ lib/lp/bugs/model/bug.py 2010-10-21 01:54:46 +0000 |
392 | @@ -101,6 +101,7 @@ |
393 | IndexedMessage, |
394 | ) |
395 | from canonical.launchpad.validators import LaunchpadValidationError |
396 | +from canonical.launchpad.webapp.authorization import check_permission |
397 | from canonical.launchpad.webapp.interfaces import ( |
398 | DEFAULT_FLAVOR, |
399 | IStoreSelector, |
400 | @@ -1540,6 +1541,12 @@ |
401 | assert IProductSeries.providedBy(target) |
402 | productseries = target |
403 | |
404 | + admins = getUtility(ILaunchpadCelebrities).admin |
405 | + if not (check_permission("launchpad.BugSupervisor", target) or |
406 | + check_permission("launchpad.Driver", target)): |
407 | + raise NominationError( |
408 | + "Only bug supervisors or owners can nominate bugs.") |
409 | + |
410 | nomination = BugNomination( |
411 | owner=owner, bug=self, distroseries=distroseries, |
412 | productseries=productseries) |
413 | |
414 | === modified file 'lib/lp/bugs/stories/bug-release-management/30-nominate-bug-for-distrorelease.txt' |
415 | --- lib/lp/bugs/stories/bug-release-management/30-nominate-bug-for-distrorelease.txt 2009-06-12 16:36:02 +0000 |
416 | +++ lib/lp/bugs/stories/bug-release-management/30-nominate-bug-for-distrorelease.txt 2010-10-21 01:54:46 +0000 |
417 | @@ -2,51 +2,67 @@ |
418 | |
419 | A bug can be nominated for a distribution release. |
420 | |
421 | - >>> user_browser.open( |
422 | - ... "http://launchpad.dev/distros/ubuntu/+source/mozilla-firefox/" |
423 | - ... "+bug/1/+nominate") |
424 | + >>> login('foo.bar@canonical.com') |
425 | + >>> nominater = factory.makePerson(name='denominater', |
426 | + ... password='g00dpassword') |
427 | + >>> poseidon = factory.makeDistribution(name='poseidon', |
428 | + ... bug_supervisor=nominater) |
429 | + >>> dsp = factory.makeDistributionSourcePackage(distribution=poseidon) |
430 | + >>> series = factory.makeDistroSeries(distribution=poseidon, |
431 | + ... name='aqua') |
432 | + >>> series = factory.makeDistroSeries(distribution=poseidon, |
433 | + ... name='hydro') |
434 | + >>> bug_task = factory.makeBugTask(target=dsp) |
435 | + >>> nominater_browser = setupBrowser( |
436 | + ... auth='Basic %s:g00dpassword' % |
437 | + ... nominater.preferredemail.email) |
438 | + >>> logout() |
439 | + >>> nominater_browser.open( |
440 | + ... "http://launchpad.dev/poseidon/+source/%s/+bug/%s/+nominate" % |
441 | + ... (dsp.name, bug_task.bug.id)) |
442 | |
443 | Before we continue, we'll set up a second browser instance, to simulate |
444 | -no-priv accessing the site from another window. Working with the same |
445 | -form in different browser windows or tabs can sometimes trigger edge |
446 | -case errors, and we'll give an example of one shortly. |
447 | - |
448 | - >>> nopriv_other_browser = setupBrowser( |
449 | - ... auth="Basic no-priv@canonical.com:test") |
450 | - >>> nopriv_other_browser.open( |
451 | - ... "http://launchpad.dev/distros/ubuntu/+source/mozilla-firefox/" |
452 | - ... "+bug/1/+nominate") |
453 | - |
454 | - >>> user_browser.getControl("Grumpy").selected = True |
455 | - >>> nopriv_other_browser.getControl("Grumpy").selected = True |
456 | - |
457 | - >>> user_browser.getControl("Submit").click() |
458 | - |
459 | - >>> for tag in find_tags_by_class(user_browser.contents, 'message'): |
460 | +the nominater accessing the site from another window. Working with the same |
461 | +form in different browser windows or tabs can sometimes trigger edge case |
462 | +errors, and we'll give an example of one shortly. |
463 | + |
464 | + >>> login('foo.bar@canonical.com') |
465 | + >>> nominater_other_browser = setupBrowser( |
466 | + ... auth='Basic %s:g00dpassword' % |
467 | + ... nominater.preferredemail.email) |
468 | + >>> logout() |
469 | + >>> nominater_other_browser.open( |
470 | + ... "http://launchpad.dev/poseidon/+source/%s/+bug/%s/+nominate" % |
471 | + ... (dsp.name, bug_task.bug.id)) |
472 | + >>> nominater_browser.getControl("Aqua").selected = True |
473 | + >>> nominater_browser.getControl("Submit").click() |
474 | + >>> for tag in find_tags_by_class(nominater_browser.contents, 'message'): |
475 | ... print tag |
476 | - <div...Added nominations for: Ubuntu Grumpy... |
477 | + <div...Added nominations for: Poseidon Aqua... |
478 | |
479 | -Now, if no-priv, having the form open in another browser window, |
480 | -accidentally nominates the bug for Grumpy a second time, an error is |
481 | +Now, if the nominater, having the form open in another browser window, |
482 | +accidentally nominates the bug for Aqua a second time, an error is |
483 | raised. |
484 | |
485 | - >>> nopriv_other_browser.getControl("Submit").click() |
486 | + >>> nominater_other_browser.getControl("Aqua").selected = True |
487 | + >>> nominater_other_browser.getControl("Submit").click() |
488 | |
489 | - >>> for tag in find_tags_by_class(nopriv_other_browser.contents, 'message'): |
490 | + >>> for tag in find_tags_by_class(nominater_other_browser.contents, |
491 | + ... 'message'): |
492 | ... print tag.renderContents() |
493 | There is 1 error. |
494 | - This bug has already been nominated for these series: Grumpy |
495 | + This bug has already been nominated for these series: Aqua |
496 | |
497 | When a nomination is submitted by a privileged user, it is immediately |
498 | approved and targeted to the release. |
499 | |
500 | >>> admin_browser.open( |
501 | - ... "http://launchpad.dev/distros/ubuntu/+source/mozilla-firefox/" |
502 | - ... "+bug/1/+nominate") |
503 | + ... "http://launchpad.dev/poseidon/+source/%s/+bug/%s/+nominate" % |
504 | + ... (dsp.name, bug_task.bug.id)) |
505 | |
506 | - >>> admin_browser.getControl("Warty").selected = True |
507 | + >>> admin_browser.getControl("Hydro").selected = True |
508 | >>> admin_browser.getControl("Submit").click() |
509 | |
510 | >>> for tag in find_tags_by_class(admin_browser.contents, 'message'): |
511 | ... print tag |
512 | - <div...Targeted bug to: Ubuntu Warty... |
513 | + <div...Targeted bug to: Poseidon Hydro... |
514 | |
515 | === modified file 'lib/lp/bugs/stories/bug-release-management/40-nominate-bug-for-productseries.txt' |
516 | --- lib/lp/bugs/stories/bug-release-management/40-nominate-bug-for-productseries.txt 2009-06-12 16:36:02 +0000 |
517 | +++ lib/lp/bugs/stories/bug-release-management/40-nominate-bug-for-productseries.txt 2010-10-21 01:54:46 +0000 |
518 | @@ -1,51 +1,64 @@ |
519 | -=Nominating a bug for a product series = |
520 | += Nominating a bug for a product series = |
521 | |
522 | A bug can be nominated for a product series. |
523 | |
524 | - >>> user_browser.open( |
525 | - ... "http://launchpad.dev/products/firefox/+bug/4/+nominate") |
526 | + >>> login('foo.bar@canonical.com') |
527 | + >>> nominater = factory.makePerson(name='nominater', |
528 | + ... password='g00dpassword') |
529 | + >>> widget = factory.makeProduct(name='widget', |
530 | + ... official_malone = True, |
531 | + ... bug_supervisor=nominater) |
532 | + >>> series = factory.makeProductSeries(product=widget, |
533 | + ... name='beta') |
534 | + >>> bug = factory.makeBug(product=widget) |
535 | + >>> nominater_browser = setupBrowser( |
536 | + ... auth='Basic %s:g00dpassword' % |
537 | + ... nominater.preferredemail.email) |
538 | + >>> logout() |
539 | + >>> nominater_browser.open( |
540 | + ... "http://launchpad.dev/widget/+bug/%s/+nominate" % bug.id) |
541 | |
542 | Before we continue, we'll set up a second browser instance, to simulate |
543 | -no-priv accessing the site from another window. Working with the same |
544 | -form in different browser windows or tabs can sometimes trigger edge |
545 | -case errors, and we'll give an example of one shortly. |
546 | - |
547 | - >>> nopriv_other_browser = setupBrowser( |
548 | - ... auth="Basic no-priv@canonical.com:test") |
549 | - >>> nopriv_other_browser.open( |
550 | - ... "http://launchpad.dev/products/firefox/+bug/4/+nominate") |
551 | - |
552 | - >>> user_browser.getControl("1.0").selected = True |
553 | - >>> nopriv_other_browser.getControl("1.0").selected = True |
554 | - |
555 | - >>> user_browser.getControl("Submit").click() |
556 | - |
557 | - >>> for tag in find_tags_by_class( |
558 | - ... user_browser.contents, 'informational message'): |
559 | +the nominater accessing the site from another window. Working with the same |
560 | +form in different browser windows or tabs can sometimes trigger edge case |
561 | +errors, and we'll give an example of one shortly. |
562 | + |
563 | + >>> login('foo.bar@canonical.com') |
564 | + >>> nominater_other_browser = setupBrowser( |
565 | + ... auth='Basic %s:g00dpassword' % |
566 | + ... nominater.preferredemail.email) |
567 | + >>> logout() |
568 | + >>> nominater_other_browser.open( |
569 | + ... "http://launchpad.dev/widget/+bug/%s/+nominate" % bug.id) |
570 | + |
571 | + >>> nominater_browser.getControl("Beta").selected = True |
572 | + >>> nominater_other_browser.getControl("Beta").selected = True |
573 | + >>> nominater_browser.getControl("Submit").click() |
574 | + |
575 | + >>> for tag in find_tags_by_class(nominater_browser.contents, 'message'): |
576 | ... print tag |
577 | - <div...Added nominations for: Mozilla Firefox 1.0... |
578 | - |
579 | -Now, if no-priv, having the form open in another browser window, |
580 | -accidentally nominates the bug for firefox 1.0 a second time, an error |
581 | -is raised. |
582 | - |
583 | - >>> nopriv_other_browser.getControl("Submit").click() |
584 | - |
585 | - >>> for tag in find_tags_by_class(nopriv_other_browser.contents, 'message'): |
586 | + <div...Added nominations for: Widget beta... |
587 | + |
588 | +Now, if the nominater, having the form open in another browser window, |
589 | +accidentally nominates the bug for Beta a second time, an error is raised. |
590 | + |
591 | + >>> nominater_other_browser.getControl("Submit").click() |
592 | + |
593 | + >>> for tag in find_tags_by_class(nominater_other_browser.contents, |
594 | + ... 'message'): |
595 | ... print tag.renderContents() |
596 | There is 1 error. |
597 | - This bug has already been nominated for these series: 1.0 |
598 | + This bug has already been nominated for these series: Beta |
599 | |
600 | When a nomination is submitted by a privileged user, it is immediately |
601 | approved and targeted to the release. |
602 | |
603 | >>> admin_browser.open( |
604 | - ... "http://launchpad.dev/products/firefox/+bug/4/+nominate") |
605 | + ... "http://launchpad.dev/widget/+bug/%s/+nominate" % bug.id) |
606 | |
607 | >>> admin_browser.getControl("Trunk").selected = True |
608 | >>> admin_browser.getControl("Submit").click() |
609 | |
610 | - >>> for tag in find_tags_by_class( |
611 | - ... admin_browser.contents, 'informational message'): |
612 | + >>> for tag in find_tags_by_class(admin_browser.contents, 'message'): |
613 | ... print tag |
614 | - <div...Targeted bug to: Mozilla Firefox trunk... |
615 | + <div...Targeted bug to: Widget trunk... |
616 | |
617 | === modified file 'lib/lp/bugs/stories/bug-release-management/60-defer-product-bug.txt' |
618 | --- lib/lp/bugs/stories/bug-release-management/60-defer-product-bug.txt 2010-06-11 21:51:48 +0000 |
619 | +++ lib/lp/bugs/stories/bug-release-management/60-defer-product-bug.txt 2010-10-21 01:54:46 +0000 |
620 | @@ -2,6 +2,11 @@ |
621 | product task is no longer editable. Instead the status is tracked |
622 | in the series task. |
623 | |
624 | + >>> admin_browser.open( |
625 | + ... "http://launchpad.dev/products/firefox/+bug/4/+nominate") |
626 | + >>> admin_browser.getControl("Trunk").selected = True |
627 | + >>> admin_browser.getControl("Submit").click() |
628 | + |
629 | >>> user_browser.open('http://bugs.launchpad.dev/firefox/+bug/4') |
630 | >>> firefox_edit_url = ( |
631 | ... 'http://bugs.launchpad.dev/firefox/+bug/4/+editstatus') |
632 | @@ -26,7 +31,8 @@ |
633 | >>> for tag in find_tags_by_class(admin_browser.contents, 'message'): |
634 | ... print tag.renderContents() |
635 | |
636 | - >>> print extract_text(find_tag_by_id(admin_browser.contents, 'bug-supervisor')) |
637 | + >>> print extract_text(find_tag_by_id(admin_browser.contents, |
638 | + ... 'bug-supervisor')) |
639 | Bug supervisor: |
640 | No Privileges Person |
641 | |
642 | |
643 | === modified file 'lib/lp/bugs/stories/bug-release-management/xx-anonymous-bug-nomination.txt' |
644 | --- lib/lp/bugs/stories/bug-release-management/xx-anonymous-bug-nomination.txt 2009-06-12 16:36:02 +0000 |
645 | +++ lib/lp/bugs/stories/bug-release-management/xx-anonymous-bug-nomination.txt 2010-10-21 01:54:46 +0000 |
646 | @@ -8,5 +8,4 @@ |
647 | >>> anon_browser.getLink('Nominate for release').click() |
648 | Traceback (most recent call last): |
649 | ... |
650 | - Unauthorized:... 'launchpad.Edit'... |
651 | - |
652 | + Unauthorized:...'launchpad.Edit'... |
653 | |
654 | === modified file 'lib/lp/bugs/stories/webservice/xx-bug.txt' |
655 | --- lib/lp/bugs/stories/webservice/xx-bug.txt 2010-10-18 22:24:59 +0000 |
656 | +++ lib/lp/bugs/stories/webservice/xx-bug.txt 2010-10-21 01:54:46 +0000 |
657 | @@ -631,8 +631,14 @@ |
658 | total_size: 0 |
659 | --- |
660 | |
661 | + >>> from zope.component import getUtility |
662 | + >>> from zope.security.proxy import removeSecurityProxy |
663 | >>> login('foo.bar@canonical.com') |
664 | >>> john = factory.makePerson(name='john') |
665 | + >>> debuntu = removeSecurityProxy(debuntu) |
666 | + >>> debuntu.bug_supervisor = john |
667 | + >>> fooix = removeSecurityProxy(fooix) |
668 | + >>> fooix.bug_supervisor = john |
669 | >>> logout() |
670 | |
671 | >>> from canonical.launchpad.testing.pages import webservice_for_person |
672 | |
673 | === modified file 'lib/lp/bugs/tests/bugs-emailinterface.txt' |
674 | --- lib/lp/bugs/tests/bugs-emailinterface.txt 2010-10-18 22:24:59 +0000 |
675 | +++ lib/lp/bugs/tests/bugs-emailinterface.txt 2010-10-21 01:54:46 +0000 |
676 | @@ -1134,6 +1134,22 @@ |
677 | Only owners, drivers and bug supervisors may assign milestones. |
678 | ... |
679 | |
680 | +Sample person must be a bug supervisor for Ubuntu and Evolution to be able to |
681 | +nominate bugs for a release. |
682 | + |
683 | + >>> from lp.registry.interfaces.distribution import IDistributionSet |
684 | + >>> from lp.testing.sampledata import (ADMIN_EMAIL) |
685 | + >>> from zope.component import getUtility |
686 | + >>> from zope.security.proxy import removeSecurityProxy |
687 | + >>> |
688 | + >>> login(ADMIN_EMAIL) |
689 | + >>> sample_person = getUtility(IPersonSet).getByEmail( |
690 | + ... "test@canonical.com") |
691 | + >>> ubuntu = getUtility(IDistributionSet).getByName("ubuntu") |
692 | + >>> ubuntu = removeSecurityProxy(ubuntu) |
693 | + >>> ubuntu.bug_supervisor = sample_person |
694 | + >>> logout() |
695 | + |
696 | >>> login('test@canonical.com') |
697 | |
698 | Like the web UI, we can assign a bug to nobody. |
699 | @@ -1331,6 +1347,13 @@ |
700 | bug task will be created, only a nomination. A general product bugtask |
701 | will be created if one doesn't exist. |
702 | |
703 | + >>> login(ADMIN_EMAIL) |
704 | + >>> no_priv = getUtility(IPersonSet).getByEmail("no-priv@canonical.com") |
705 | + >>> evolution = getUtility(IProductSet).getByName("evolution") |
706 | + >>> evolution = removeSecurityProxy(evolution) |
707 | + >>> evolution.bug_supervisor = no_priv |
708 | + >>> logout() |
709 | + |
710 | >>> login('no-priv@canonical.com') |
711 | >>> bug = new_firefox_bug() |
712 | >>> submit_commands(bug, 'affects /evolution/trunk') |
713 | |
714 | === added file 'lib/lp/bugs/tests/test_bugsupervisor_bugnomination.py' |
715 | --- lib/lp/bugs/tests/test_bugsupervisor_bugnomination.py 1970-01-01 00:00:00 +0000 |
716 | +++ lib/lp/bugs/tests/test_bugsupervisor_bugnomination.py 2010-10-21 01:54:46 +0000 |
717 | @@ -0,0 +1,85 @@ |
718 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
719 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
720 | + |
721 | +"""Tests related to bug nominations for an object with a bug supervisor.""" |
722 | + |
723 | +__metaclass__ = type |
724 | + |
725 | +from canonical.launchpad.ftests import ( |
726 | + login, |
727 | + login_person, |
728 | + logout, |
729 | + ) |
730 | +from canonical.testing.layers import DatabaseFunctionalLayer |
731 | + |
732 | +from lp.bugs.interfaces.bugnomination import NominationError |
733 | +from lp.testing import TestCaseWithFactory |
734 | + |
735 | + |
736 | +class AddNominationTestMixin: |
737 | + """Test case mixin for IBug.addNomination.""" |
738 | + |
739 | + layer = DatabaseFunctionalLayer |
740 | + |
741 | + def setUp(self): |
742 | + super(AddNominationTestMixin, self).setUp() |
743 | + login('foo.bar@canonical.com') |
744 | + self.user = self.factory.makePerson(name='ordinary-user') |
745 | + self.bug_supervisor = self.factory.makePerson(name='no-ordinary-user') |
746 | + self.owner = self.factory.makePerson(name='extraordinary-user') |
747 | + self.setUpTarget() |
748 | + logout() |
749 | + |
750 | + def tearDown(self): |
751 | + logout() |
752 | + super(AddNominationTestMixin, self).tearDown() |
753 | + |
754 | + def test_user_addNominationFor_series(self): |
755 | + # A bug may not be nominated for a series of a product with an |
756 | + # existing task by just anyone. |
757 | + login_person(self.user) |
758 | + self.assertRaises(NominationError, |
759 | + self.bug.addNomination, self.user, self.series) |
760 | + |
761 | + def test_bugsupervisor_addNominationFor_series(self): |
762 | + # A bug may be nominated for a series of a product with an |
763 | + # exisiting task by the product's bug supervisor. |
764 | + login_person(self.bug_supervisor) |
765 | + self.bug.addNomination(self.bug_supervisor, self.series) |
766 | + self.assertTrue(len(self.bug.getNominations()), 1) |
767 | + |
768 | + def test_owner_addNominationFor_series(self): |
769 | + # A bug may be nominated for a series of a product with an |
770 | + # exisiting task by the product's owner. |
771 | + login_person(self.owner) |
772 | + self.bug.addNomination(self.owner, self.series) |
773 | + self.assertTrue(len(self.bug.getNominations()), 1) |
774 | + |
775 | + |
776 | +class TestBugAddNominationProductSeries( |
777 | + AddNominationTestMixin, TestCaseWithFactory): |
778 | + """Test IBug.addNomination for IProductSeries nominations.""" |
779 | + |
780 | + def setUpTarget(self): |
781 | + self.product = self.factory.makeProduct(official_malone = True, |
782 | + bug_supervisor=self.bug_supervisor, |
783 | + owner=self.owner) |
784 | + self.series = self.factory.makeProductSeries(product=self.product) |
785 | + self.bug = self.factory.makeBug(product=self.product) |
786 | + self.milestone = self.factory.makeMilestone(productseries=self.series) |
787 | + |
788 | + |
789 | +class TestBugAddNominationDistroSeries( |
790 | + AddNominationTestMixin, TestCaseWithFactory): |
791 | + """Test IBug.addNomination for IDistroSeries nominations.""" |
792 | + |
793 | + def setUpTarget(self): |
794 | + self.distro = self.factory.makeDistribution( |
795 | + bug_supervisor=self.bug_supervisor, |
796 | + owner=self.owner) |
797 | + self.series = self.factory.makeDistroRelease(distribution=self.distro) |
798 | + # The factory can't create a distro bug directly. |
799 | + self.bug = self.factory.makeBug() |
800 | + self.bug.addTask(self.bug_supervisor, self.distro) |
801 | + self.milestone = self.factory.makeMilestone( |
802 | + distribution=self.distro) |
803 | |
804 | === modified file 'lib/lp/registry/interfaces/distribution.py' |
805 | --- lib/lp/registry/interfaces/distribution.py 2010-08-20 20:31:18 +0000 |
806 | +++ lib/lp/registry/interfaces/distribution.py 2010-10-21 01:54:46 +0000 |
807 | @@ -670,7 +670,7 @@ |
808 | |
809 | def new(name, displayname, title, description, summary, domainname, |
810 | members, owner, mugshot=None, logo=None, icon=None): |
811 | - """Creaste a new distribution.""" |
812 | + """Create a new distribution.""" |
813 | |
814 | |
815 | class NoSuchDistribution(NameLookupFailed): |
816 | |
817 | === modified file 'lib/lp/testing/factory.py' |
818 | --- lib/lp/testing/factory.py 2010-10-19 22:07:04 +0000 |
819 | +++ lib/lp/testing/factory.py 2010-10-21 01:54:46 +0000 |
820 | @@ -1849,7 +1849,8 @@ |
821 | return library_file_alias |
822 | |
823 | def makeDistribution(self, name=None, displayname=None, owner=None, |
824 | - members=None, title=None, aliases=None): |
825 | + members=None, title=None, aliases=None, |
826 | + bug_supervisor=None): |
827 | """Make a new distribution.""" |
828 | if name is None: |
829 | name = self.getUniqueString(prefix="distribution") |
830 | @@ -1869,6 +1870,9 @@ |
831 | members, owner) |
832 | if aliases is not None: |
833 | removeSecurityProxy(distro).setAliases(aliases) |
834 | + if bug_supervisor is not None: |
835 | + naked_distro = removeSecurityProxy(distro) |
836 | + naked_distro.bug_supervisor = bug_supervisor |
837 | return distro |
838 | |
839 | def makeDistroRelease(self, distribution=None, version=None, |
Per IRC discussion, please change this so that the permission is only provided for bug supervisors.