Merge lp:~adeuring/launchpad/bug-479331 into lp:launchpad

Proposed by Abel Deuring
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~adeuring/launchpad/bug-479331
Merge into: lp:launchpad
Diff against target: 444 lines (+207/-69)
8 files modified
lib/canonical/launchpad/database/structuralsubscription.py (+5/-5)
lib/canonical/launchpad/interfaces/structuralsubscription.py (+3/-0)
lib/lp/bugs/browser/bugsupervisor.py (+29/-14)
lib/lp/bugs/stories/initial-bug-contacts/05-set-distribution-bugcontact.txt (+76/-28)
lib/lp/bugs/stories/initial-bug-contacts/10-set-upstream-bugcontact.txt (+82/-14)
lib/lp/bugs/stories/initial-bug-contacts/20-file-upstream-bug.txt (+5/-4)
lib/lp/bugs/stories/initial-bug-contacts/25-file-distribution-bug.txt (+3/-2)
lib/lp/registry/configure.zcml (+4/-2)
To merge this branch: bzr merge lp:~adeuring/launchpad/bug-479331
Reviewer Review Type Date Requested Status
Graham Binns (community) Approve
Review via email: mp+14783@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Abel Deuring (adeuring) wrote :

This branch fixes (sort of) bug 479331: It's not possible to set someone else as bug supervisor.

While the bug description says that anybody should be able to appoint the bug supervisor, it seems for now better to keep the policy of the structural bug subscription which is autmatically performed for the new supervisor: People may only subscribe themselves and teams they administer.

Firstly, the policy for structural bug subscription is reasonable: people who are not aware of the consequences of becoming a bug supervisor might feel being spammed by the possibly huge amount of bug mails they suddenly receive.

Secondly, the option to simply allow anybody to appoint a bug supervisor would mean to implement another exception to the subscription permission check in StructuralSubscriptionTargetMixin.userCanAlterSubscription(), which is already slightly too complicated for my taste, with the special handling of IDistributionSourcePackage targets.

So I only added a check if the person which tries to appoint the bug supervisor can actually do this: userCanAlterSubscription() is now called in BugSupervisorEditView.validate(). This involved to rename the method _userCanAlterSubscription(), to ad it to the interface class and to change two ZCML files.

The problem that most people cannot appoint others as bug supervisors was not caught in the tests because the persons used in the tests were all Launchpad administrators, which are allowed to make structural subscriptions for others. So I had to change the "test persons", and I had to change the teams they can subscribe. (BTW, our test data has way too many LP administrators: I needed some time to find persons who are team admins but not LP admins.)

The changed and added tests quickly re-appoint several persons and team as bug supervisors. When a new bug supervisor is appointed, the structural of the old supervisor is not automatically removed. The set of people subscribed are shown in other page tests, so these sets changed too: I did not bother add anything to remove the subscription.

test:./bin/test -v -f lp stories.initial-bug-contacts

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/canonical/launchpad/database/structuralsubscription.py
  lib/canonical/launchpad/interfaces/structuralsubscription.py
  lib/lp/bugs/browser/bugsupervisor.py
  lib/lp/bugs/stories/initial-bug-contacts/05-set-distribution-bugcontact.txt
  lib/lp/bugs/stories/initial-bug-contacts/10-set-upstream-bugcontact.txt
  lib/lp/bugs/stories/initial-bug-contacts/20-file-upstream-bug.txt
  lib/lp/bugs/stories/initial-bug-contacts/25-file-distribution-bug.txt
  lib/lp/registry/configure.zcml

== Pylint notices ==

lib/canonical/launchpad/interfaces/structuralsubscription.py
    22: [F0401] Unable to import 'lazr.enum' (No module named enum)
    29: [F0401] Unable to import 'lazr.restful.declarations' (No module named restful)
    34: [F0401] Unable to import 'lazr.restful.fields' (No module named restful)

Revision history for this message
Graham Binns (gmb) wrote :
Download full text (7.4 KiB)

Hi Abel,

Nice branch. Couple of minor issues to deal with - nothing major - and the
error message change that we discussed on IRC. Other than that, this is
good to go.

> === modified file 'lib/lp/bugs/browser/bugsupervisor.py'
> --- lib/lp/bugs/browser/bugsupervisor.py 2009-09-09 13:37:24 +0000
> +++ lib/lp/bugs/browser/bugsupervisor.py 2009-11-12 12:30:31 +0000
> @@ -11,7 +11,6 @@
> from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
> from canonical.launchpad.webapp import (
> action, canonical_url, LaunchpadEditFormView)
> -from canonical.launchpad.webapp.authorization import check_permission
> from canonical.launchpad.webapp.menu import structured
>
>
> @@ -91,19 +90,31 @@
>
> supervisor = data['bug_supervisor']
>
> - if (supervisor is not None and supervisor.isTeam() and
> - supervisor not in self.user.getAdministratedTeams() and not
> - check_permission('launchpad.Admin', self.user)):
> - error = structured(
> - "You cannot set %(team)s as the bug supervisor for "
> - "%(target)s because you are not an administrator of that "
> - "team.<br />If you believe that %(team)s should be the bug"
> - " supervisor for %(target)s, please notify one of the "
> - "<a href=\"%(url)s\">%(team)s administrators</a>.",
> - team=supervisor.displayname,
> - target=self.context.displayname,
> - url=(canonical_url(supervisor, rootsite='mainsite') +
> - '/+members'))
> + # Making a person the bug supervisor implies subscribing him
> + # to all bug mail. Ensure that the current user can indeed
> + # do this.
> + if (supervisor is not None and
> + not self.context.userCanAlterSubscription(supervisor, self.user)):
> + if supervisor.isTeam():
> + error = structured(
> + "You cannot set %(team)s as the bug supervisor for "
> + "%(target)s because you are not an administrator of that "
> + "team.<br />If you believe that %(team)s should be the "
> + "bug supervisor for %(target)s, please notify one of the "
> + "<a href=\"%(url)s\">%(team)s administrators</a>.",
> + team=supervisor.displayname,
> + target=self.context.displayname,
> + url=(canonical_url(supervisor, rootsite='mainsite') +
> + '/+members'))
> + self.setFieldError('bug_supervisor', error)
> + else:
> + error = structured(
> + "You cannot set another person as the bug supervisor for "
> + "%(target)s.<br/> If you believe that %(person)s should "
> + "be the bug supervisor for %(target)s, please ask this "
> + "person to make itself the bug supervisor.",

As we discussed on IRC, this error message needs to be changed, since
people who aren't project admins or members of admin teams can't set
themselves as bug supervisor.

> + ...

Read more...

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/database/structuralsubscription.py'
2--- lib/canonical/launchpad/database/structuralsubscription.py 2009-10-28 19:11:17 +0000
3+++ lib/canonical/launchpad/database/structuralsubscription.py 2009-11-12 20:26:12 +0000
4@@ -22,7 +22,7 @@
5 IStructuralSubscription, IStructuralSubscriptionTarget,
6 UserCannotSubscribePerson)
7 from lp.registry.interfaces.person import (
8- IPerson, validate_public_person, validate_person_not_private_membership)
9+ validate_public_person, validate_person_not_private_membership)
10
11
12 class StructuralSubscription(SQLBase):
13@@ -131,8 +131,8 @@
14 '%s is not a valid structural subscription target.')
15 return args
16
17- def _userCanAlterSubscription(self, subscriber, subscribed_by):
18- """Check if a user can change a subscription for a person."""
19+ def userCanAlterSubscription(self, subscriber, subscribed_by):
20+ """See `IStructuralSubscriptionTarget`."""
21 # A Launchpad administrator or the user can subscribe a user.
22 # A Launchpad or team admin can subscribe a team.
23
24@@ -155,7 +155,7 @@
25 if subscriber is None:
26 subscriber = subscribed_by
27
28- if not self._userCanAlterSubscription(subscriber, subscribed_by):
29+ if not self.userCanAlterSubscription(subscriber, subscribed_by):
30 raise UserCannotSubscribePerson(
31 '%s does not have permission to subscribe %s.' % (
32 subscribed_by.name, subscriber.name))
33@@ -185,7 +185,7 @@
34 if subscriber is None:
35 subscriber = unsubscribed_by
36
37- if not self._userCanAlterSubscription(subscriber, unsubscribed_by):
38+ if not self.userCanAlterSubscription(subscriber, unsubscribed_by):
39 raise UserCannotSubscribePerson(
40 '%s does not have permission to unsubscribe %s.' % (
41 unsubscribed_by.name, subscriber.name))
42
43=== modified file 'lib/canonical/launchpad/interfaces/structuralsubscription.py'
44--- lib/canonical/launchpad/interfaces/structuralsubscription.py 2009-10-08 12:50:25 +0000
45+++ lib/canonical/launchpad/interfaces/structuralsubscription.py 2009-11-12 20:26:12 +0000
46@@ -163,6 +163,9 @@
47 parent_subscription_target = Attribute(
48 "The target's parent, or None if one doesn't exist.")
49
50+ def userCanAlterSubscription(subscriber, subscribed_by):
51+ """Check if a user can change a subscription for a person."""
52+
53 def addSubscription(subscriber, subscribed_by):
54 """Add a subscription for this structure.
55
56
57=== modified file 'lib/lp/bugs/browser/bugsupervisor.py'
58--- lib/lp/bugs/browser/bugsupervisor.py 2009-09-09 13:37:24 +0000
59+++ lib/lp/bugs/browser/bugsupervisor.py 2009-11-12 20:26:12 +0000
60@@ -11,7 +11,6 @@
61 from lp.bugs.interfaces.bugsupervisor import IHasBugSupervisor
62 from canonical.launchpad.webapp import (
63 action, canonical_url, LaunchpadEditFormView)
64-from canonical.launchpad.webapp.authorization import check_permission
65 from canonical.launchpad.webapp.menu import structured
66
67
68@@ -91,19 +90,35 @@
69
70 supervisor = data['bug_supervisor']
71
72- if (supervisor is not None and supervisor.isTeam() and
73- supervisor not in self.user.getAdministratedTeams() and not
74- check_permission('launchpad.Admin', self.user)):
75- error = structured(
76- "You cannot set %(team)s as the bug supervisor for "
77- "%(target)s because you are not an administrator of that "
78- "team.<br />If you believe that %(team)s should be the bug"
79- " supervisor for %(target)s, please notify one of the "
80- "<a href=\"%(url)s\">%(team)s administrators</a>.",
81- team=supervisor.displayname,
82- target=self.context.displayname,
83- url=(canonical_url(supervisor, rootsite='mainsite') +
84- '/+members'))
85+ # Making a person the bug supervisor implies subscribing him
86+ # to all bug mail. Ensure that the current user can indeed
87+ # do this.
88+ if (supervisor is not None and
89+ not self.context.userCanAlterSubscription(supervisor, self.user)):
90+ if supervisor.isTeam():
91+ error = structured(
92+ "You cannot set %(team)s as the bug supervisor for "
93+ "%(target)s because you are not an administrator of that "
94+ "team.<br />If you believe that %(team)s should be the "
95+ "bug supervisor for %(target)s, please notify one of the "
96+ "<a href=\"%(url)s\">%(team)s administrators</a>. See "
97+ "<a href=\"https://help.launchpad.net/BugSupervisors\">"
98+ "the help wiki</a> for information about setting a bug "
99+ "supervisor.",
100+ team=supervisor.displayname,
101+ target=self.context.displayname,
102+ url=(canonical_url(supervisor, rootsite='mainsite') +
103+ '/+members'))
104+ self.setFieldError('bug_supervisor', error)
105+ else:
106+ error = structured(
107+ "You cannot set another person as the bug supervisor for "
108+ "%(target)s.<br />See "
109+ "<a href=\"https://help.launchpad.net/BugSupervisors\">"
110+ "the help wiki</a> for information about setting a bug "
111+ "supervisor.",
112+ person=supervisor.displayname,
113+ target=self.context.displayname)
114 self.setFieldError('bug_supervisor', error)
115
116 def cancel_url(self):
117
118=== modified file 'lib/lp/bugs/stories/initial-bug-contacts/05-set-distribution-bugcontact.txt'
119--- lib/lp/bugs/stories/initial-bug-contacts/05-set-distribution-bugcontact.txt 2009-08-13 19:03:36 +0000
120+++ lib/lp/bugs/stories/initial-bug-contacts/05-set-distribution-bugcontact.txt 2009-11-12 20:26:12 +0000
121@@ -11,57 +11,105 @@
122 ...
123 LinkNotFoundError
124
125-Now we login as Mark...
126+Now we login as Colin...
127
128- >>> browser.addHeader('Authorization', 'Basic mark@example.com:test')
129- >>> browser.reload()
130- >>> browser.url
131- 'http://launchpad.dev/ubuntu/+bugs'
132+ >>> colin_browser = setupBrowser(
133+ ... auth='Basic colin.watson@ubuntulinux.com:test')
134+ >>> colin_browser.open('http://launchpad.dev/ubuntu/+bugs')
135
136 ...and we can see that the link is there.
137
138- >>> bug_supervisor_link = browser.getLink("Change bug supervisor")
139+ >>> bug_supervisor_link = colin_browser.getLink("Change bug supervisor")
140 >>> bug_supervisor_link.url
141 'http://launchpad.dev/ubuntu/+bugsupervisor'
142
143 Anyone with launchpad.Edit permission can edit the distribution bug
144-supervisor. In this example, we're using mark@example.com to set the
145-distribution bug supervisor.
146+supervisor, but most users can select only themselves and the teams they
147+administer. In this example, Colin will set himself as the distribution
148+bug supervisor.
149
150 >>> bug_supervisor_link.click()
151- >>> browser.url
152+ >>> colin_browser.url
153 'http://launchpad.dev/ubuntu/+bugsupervisor'
154
155 The bug supervisor page takes just one simple value: the bug supervisor email
156-or nickname. Let's set carlos@canonical.com as the bug supervisor for
157+or nickname. Let's set colin.watson@ubuntulinux.com as the bug supervisor for
158 Ubuntu.
159
160- >>> browser.getControl("Bug Supervisor").value = 'carlos@canonical.com'
161- >>> browser.getControl("Change").click()
162+ >>> colin_browser.getControl("Bug Supervisor").value = (
163+ ... ' colin.watson@ubuntulinux.com ')
164+ >>> colin_browser.getControl("Change").click()
165
166 And then we're redirected to the distribution page.
167
168- >>> browser.url
169+ >>> colin_browser.url
170 'http://launchpad.dev/ubuntu'
171
172- >>> for tag in find_tags_by_class(browser.contents, "informational message"):
173+ >>> for tag in find_tags_by_class(colin_browser.contents,
174+ ... "informational message"):
175 ... print tag
176- <div...Successfully changed the bug supervisor to ...Carlos Perelló Marín...
177+ <div...Successfully changed the bug supervisor to ...Colin Watson...
178
179 Now that the bug supervisor is set, there's a link to him on the
180-distribution page.
181+distribution's bug page page.
182
183- >>> browser.getLink(
184- ... url='/~carlos').url
185- 'http://launchpad.dev/~carlos'
186+ >>> browser.open('http://bugs.launchpad.dev/ubuntu')
187+ >>> browser.getLink(url='/~kamion').url
188+ 'http://launchpad.dev/~kamion'
189
190 When the bug supervisor doesn't have an email address, the displayname is
191-shown instead in the feedback message.
192-
193- >>> browser.open("http://launchpad.dev/ubuntu/+bugsupervisor")
194- >>> browser.getControl("Bug Supervisor").value = 'ubuntu-team'
195- >>> browser.getControl("Change").click()
196-
197- >>> for tag in find_tags_by_class(browser.contents, "informational message"):
198- ... print tag
199- <div...Successfully changed the bug supervisor to ...Ubuntu Team...
200+shown instead in the feedback message. Let's appoint the Ubuntu Team as
201+the bug supervisor. Since Colin is an administrator of this team, he can
202+do it.
203+
204+ >>> colin_browser.open("http://launchpad.dev/ubuntu/+bugsupervisor")
205+ >>> colin_browser.getControl("Bug Supervisor").value = 'ubuntu-team'
206+ >>> colin_browser.getControl("Change").click()
207+
208+ >>> print extract_text(colin_browser.contents)
209+ Ubuntu Linux in Launchpad
210+ ...
211+ Successfully changed the bug supervisor to Ubuntu Team.
212+ ...
213+
214+While anybody with edit permissions can set the bug supervisor, only
215+certain persons can be successfully selected. All users can set
216+themselves self and teams they administer as the bug supervisor, as
217+shown above. But ordinary users cannot appoint other persons and teams
218+they do not administer.
219+
220+ >>> colin_browser.open("http://launchpad.dev/ubuntu/+bugsupervisor")
221+ >>> colin_browser.getControl("Bug Supervisor").value = 'admins'
222+ >>> colin_browser.getControl("Change").click()
223+ >>> print extract_text(colin_browser.contents)
224+ Edit bug supervisor for Ubuntu : Ubuntu
225+ ...
226+ You cannot set Launchpad Administrators as the bug supervisor for
227+ Ubuntu because you are not an administrator of that team.
228+ If you believe that Launchpad Administrators should be the bug
229+ supervisor for Ubuntu, please notify one of the Launchpad
230+ Administrators administrators.
231+ ...
232+
233+But members of the Launchpad administration team can appoint anybody
234+as bug supervisors, teams...
235+
236+ >>> admin_browser.open("http://launchpad.dev/ubuntu/+bugsupervisor")
237+ >>> admin_browser.getControl("Bug Supervisor").value = 'ubuntu-translators'
238+ >>> admin_browser.getControl("Change").click()
239+ >>> print extract_text(admin_browser.contents)
240+ Ubuntu Linux in Launchpad
241+ ...
242+ Successfully changed the bug supervisor to Ubuntu Translators.
243+ ...
244+
245+...as well as persons.
246+
247+ >>> admin_browser.open("http://launchpad.dev/ubuntu/+bugsupervisor")
248+ >>> admin_browser.getControl("Bug Supervisor").value = 'carlos'
249+ >>> admin_browser.getControl("Change").click()
250+ >>> print extract_text(admin_browser.contents)
251+ Ubuntu Linux in Launchpad
252+ ...
253+ Successfully changed the bug supervisor to Carlos Perelló Marín.
254+ ...
255
256=== modified file 'lib/lp/bugs/stories/initial-bug-contacts/10-set-upstream-bugcontact.txt'
257--- lib/lp/bugs/stories/initial-bug-contacts/10-set-upstream-bugcontact.txt 2009-08-13 19:03:36 +0000
258+++ lib/lp/bugs/stories/initial-bug-contacts/10-set-upstream-bugcontact.txt 2009-11-12 20:26:12 +0000
259@@ -1,20 +1,91 @@
260 == Setting Upstream Bug Supervisor ==
261
262 Setting the bug supervisor for an upstream requires launchpad.Edit
263-permission on the product.
264-
265- >>> browser.addHeader("Authorization", "Basic mark@example.com:test")
266-
267- >>> browser.open("http://localhost:9000/firefox/+bugsupervisor")
268- >>> browser.getControl(name="field.bug_supervisor").value = (
269- ... "robertc@robertcollins.net")
270- >>> browser.getControl("Change").click()
271+permission on the product. But regular users can only appoint
272+themselves as bug supervisors and teams they administer.
273+
274+ >>> sample_browser = setupBrowser()
275+ >>> sample_browser.addHeader("Authorization",
276+ ... "Basic test@canonical.com:test")
277+
278+ >>> sample_browser.open("http://localhost:9000/firefox/+bugsupervisor")
279+ >>> sample_browser.getControl(name="field.bug_supervisor").value = (
280+ ... "test@canonical.com")
281+ >>> sample_browser.getControl("Change").click()
282+
283+He is now redircted to the main product page, and he sees a confirmation
284+message.
285+
286+ >>> print sample_browser.url
287+ http://localhost:9000/firefox
288+ >>> print extract_text(sample_browser.contents)
289+ Mozilla Firefox in Launchpad
290+ ...
291+ Successfully changed the bug supervisor to Sample Person.
292+ ...
293
294 Another example, this time with a team that has no "preferred email" set.
295
296- >>> browser.open("http://localhost:9000/firefox/+bugsupervisor")
297- >>> browser.getControl(name="field.bug_supervisor").value = "guadamen"
298- >>> browser.getControl("Change").click()
299+ >>> sample_browser.open("http://localhost:9000/firefox/+bugsupervisor")
300+ >>> sample_browser.getControl(name="field.bug_supervisor").value = (
301+ ... "landscape-developers")
302+ >>> sample_browser.getControl("Change").click()
303+ >>> print sample_browser.url
304+ http://localhost:9000/firefox
305+ >>> print extract_text(sample_browser.contents)
306+ Mozilla Firefox in Launchpad
307+ ...
308+ Successfully changed the bug supervisor to Landscape Developers.
309+ ...
310+
311+If Sample Person tries to appoint another person, he'll get an error message.
312+
313+ >>> sample_browser.open("http://localhost:9000/firefox/+bugsupervisor")
314+ >>> sample_browser.getControl(name="field.bug_supervisor").value = (
315+ ... "robertc@robertcollins.net")
316+ >>> sample_browser.getControl("Change").click()
317+ >>> print sample_browser.url
318+ http://localhost:9000/firefox/+bugsupervisor
319+ >>> print extract_text(sample_browser.contents)
320+ Edit bug supervisor for Mozilla Firefox : Mozilla Firefox
321+ ...
322+ You cannot set another person as the bug supervisor for Mozilla Firefox.
323+ See the help wiki for information about setting a bug supervisor.
324+ The person or team responsible for bug management.
325+ ...
326+
327+If he tries to appoint a team which he does not administer, he'll get
328+too an an error message.
329+
330+ >>> sample_browser.getControl(name="field.bug_supervisor").value = (
331+ ... "guadamen")
332+ >>> sample_browser.getControl("Change").click()
333+ >>> print sample_browser.url
334+ http://localhost:9000/firefox/+bugsupervisor
335+ >>> print extract_text(sample_browser.contents)
336+ Edit bug supervisor for Mozilla Firefox : Mozilla Firefox
337+ ...
338+ You cannot set GuadaMen as the bug supervisor for Mozilla Firefox
339+ because you are not an administrator of that team.
340+ If you believe that GuadaMen should be the bug supervisor for Mozilla
341+ Firefox, please notify one of the GuadaMen administrators.
342+ ...
343+
344+Launchpad administrators can appoint anybody.
345+
346+ >>> admin_browser = setupBrowser()
347+ >>> admin_browser.addHeader("Authorization",
348+ ... "Basic foo.bar@canonical.com:test")
349+
350+ >>> admin_browser.open("http://localhost:9000/firefox/+bugsupervisor")
351+ >>> admin_browser.getControl(name="field.bug_supervisor").value = (
352+ ... "robertc@robertcollins.net")
353+ >>> admin_browser.getControl("Change").click()
354+ >>> print extract_text(admin_browser.contents)
355+ Mozilla Firefox in Launchpad
356+ ...
357+ Successfully changed the bug supervisor to Robert Collins.
358+ ...
359
360
361 = Teams as Bug Supervisors =
362@@ -22,9 +93,6 @@
363 First, some setup. We create a new product owned by "Sample Person", using a
364 separate browser instance to avoid breaking the other page tests.
365
366- >>> sample_browser = setupBrowser()
367- >>> sample_browser.addHeader("Authorization",
368- ... "Basic test@canonical.com:test")
369 >>> sample_browser.open('http://launchpad.dev/projects/+new')
370 >>> sample_browser.getControl('URL').value = 'testy'
371 >>> sample_browser.getControl('Name').value = 'Test Product'
372
373=== modified file 'lib/lp/bugs/stories/initial-bug-contacts/20-file-upstream-bug.txt'
374--- lib/lp/bugs/stories/initial-bug-contacts/20-file-upstream-bug.txt 2009-08-13 19:03:36 +0000
375+++ lib/lp/bugs/stories/initial-bug-contacts/20-file-upstream-bug.txt 2009-11-12 20:26:12 +0000
376@@ -15,9 +15,9 @@
377 >>> print browser.url.replace(bug_id, "BUG-ID")
378 http://bugs.launchpad.dev/firefox/+bug/BUG-ID
379
380-Now mark (because he's the bug reporter), guadamen (because they are
381-the product bug supervisor), and lifeless (because he is a subscriber to
382-mozilla) are subscribed to this bug:
383+Now mark (because he's the bug reporter), Sample Person (a former bug
384+supervisor), Landscape Developers (another former bug supervisor) and
385+Robert Collins (the current bug supervisor) are subscribed to this bug:
386
387 >>> from zope.component import getUtility
388 >>> from canonical.launchpad.interfaces import IBugSet
389@@ -31,7 +31,8 @@
390 >>> login(ANONYMOUS)
391 >>> bugset = getUtility(IBugSet)
392 >>> subscriber_names(bugset.get(bug_id))
393- [u'GuadaMen', u'Mark Shuttleworth', u'Robert Collins']
394+ [u'Landscape Developers', u'Mark Shuttleworth', u'Robert Collins',
395+ u'Sample Person']
396 >>> logout()
397
398 For a security bug, only the reporter and the registrant gets
399
400=== modified file 'lib/lp/bugs/stories/initial-bug-contacts/25-file-distribution-bug.txt'
401--- lib/lp/bugs/stories/initial-bug-contacts/25-file-distribution-bug.txt 2009-08-14 18:02:29 +0000
402+++ lib/lp/bugs/stories/initial-bug-contacts/25-file-distribution-bug.txt 2009-11-12 20:26:12 +0000
403@@ -19,7 +19,7 @@
404 >>> print browser.url.replace(bug_id, "BUG-ID")
405 http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/BUG-ID
406
407-We should have four subscribers now. The bug reporter (also a package
408+We should have six subscribers now. The bug reporter (also a package
409 subscriber), mark, the distro bug supervisor, ubuntu-team, and carlos
410 and foobar, who are subscribed to the distribution.
411
412@@ -36,7 +36,8 @@
413
414 >>> bugset = getUtility(IBugSet)
415 >>> subscriber_names(bugset.get(bug_id))
416- [u'carlos', u'mark', u'name16', u'ubuntu-team']
417+ [u'carlos', u'kamion', u'mark', u'name16', u'ubuntu-team',
418+ u'ubuntu-translators']
419
420 >>> logout()
421
422
423=== modified file 'lib/lp/registry/configure.zcml'
424--- lib/lp/registry/configure.zcml 2009-10-23 22:09:49 +0000
425+++ lib/lp/registry/configure.zcml 2009-11-12 20:26:12 +0000
426@@ -1162,7 +1162,8 @@
427 attributes="
428 addSubscription
429 addBugSubscription
430- removeBugSubscription"/>
431+ removeBugSubscription
432+ userCanAlterSubscription"/>
433
434 <!-- IHasBugSupervisor -->
435
436@@ -1421,7 +1422,8 @@
437 attributes="
438 addSubscription
439 addBugSubscription
440- removeBugSubscription"/>
441+ removeBugSubscription
442+ userCanAlterSubscription"/>
443
444 <!-- IHasBugSupervisor -->
445