Merge lp:~intellectronica/launchpad/bugmail-ui-fixes into lp:launchpad
- bugmail-ui-fixes
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Gavin Panella |
Approved revision: | no longer in the source branch. |
Merged at revision: | not available |
Proposed branch: | lp:~intellectronica/launchpad/bugmail-ui-fixes |
Merge into: | lp:launchpad |
Diff against target: |
390 lines 10 files modified
lib/canonical/launchpad/browser/structuralsubscription.py (+30/-1) lib/canonical/launchpad/database/structuralsubscription.py (+11/-0) lib/canonical/launchpad/interfaces/structuralsubscription.py (+3/-0) lib/lp/registry/browser/distribution.py (+2/-5) lib/lp/registry/browser/distroseries.py (+5/-10) lib/lp/registry/browser/milestone.py (+2/-6) lib/lp/registry/browser/product.py (+3/-10) lib/lp/registry/browser/productseries.py (+5/-12) lib/lp/registry/configure.zcml (+14/-7) lib/lp/registry/model/milestone.py (+4/-0) |
To merge this branch: | bzr merge lp:~intellectronica/launchpad/bugmail-ui-fixes |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | code | Approve | |
Martin Albisetti (community) | ui | Approve | |
Michael Nelson (community) | ui | Approve | |
Canonical Launchpad Engineering | code ui | Pending | |
Review via email: mp+13067@code.launchpad.net |
Commit message
Description of the change
Eleanor Berger (intellectronica) wrote : | # |
Michael Nelson (michael.nelson) wrote : | # |
> This branch makes the menu link for subscribing to bug mail sensitive to the
> user's existing subsription. If the user is already subscribed (directly, or
> via one of the teams he's a member of), the link displays the edit icon and
> offers to edit subscription. If no subscription exists the link offers to
> subscribe and displays the add icon.
Nice change Tom! That'll make it much easier for people to follow when they want to edit or remove subscriptions. I actually had to revert your changes to see what happens currently because what you've done is so natural.
Two things on that page that are unrelated to your change, but might be nice to look at, here's a screenshot to show them:
http://
1. There seems to be an empty portlet below? It looks like some ajax is meant to load something there - I'm guessing you're already aware and there's probably already a bug about it.
2. Is there any reason why the "Bug tracker: Launchpad, Bug supervisor:...." section is sitting over on the right like that? It looks exactly like the type of info that is usually a main-area portlet with an h2 heading and a dl (like the Project information at https:/
[snip]
>
> While working on this I realised that the form for editing structural
> subscriptions does not behave in the expected way, like all other boomerang
> forms. It doesn't offer a cancel link, and after submitting it displays the
> form again, instead of returning to the context. I plan to fix that in a
> future branch.
Great - A few other things to consider on that page when you get to it (in case you didn't already notice them).
First, the heading/breadcrumbs are currently:
Subscribe to Bugs in Jokosher Audio Editor
Jokosher >> Bugs >> Subscribe to Bugs in Jokosher Audio E...
My preference here would be for:
Subscribe to Bugs in Jokosher Audio Editor
Jokosher >> Bugs >> Subscribe
but I've sent an email out to the dev list about this yesterday, and it seems that the consensus is for:
Subscribe
Jokosher >> Bugs >> Subscribe
If you think that ^^ would be wrong, please voice your opinion on the email list :)
Secondly, that tip about "Managing Launchpad Bugs e-mail really doesn't belong there in the side-bar?
Anyway, they're unrelated to this branch. Great work!
Martin Albisetti (beuno) wrote : | # |
Nothing to add!
Gavin Panella (allenap) wrote : | # |
<allenap> intellectronica: I really like your bugmail-ui-fixes branch :)
<allenap> intellectronica: Two questions.
<allenap> intellectronica: Line 49, when it does self.context.
<intellectronica> allenap: exactly. context can be either the target itself or a view
<allenap> intellectronica: Could you add a comment to the else: clause to say that?
<intellectronica> allenap: sure, that's a good idea
<allenap> intellectronica: Question the second. The zcml. Is there an common interface where you can declare the permissions for userHasBugSubsc
<intellectronica> allenap: i wish. but no, that's not possible because of the stupid way in which zcml handles the interaction between permissions specified by interface and byb attributes
<allenap> intellectronica: Okay. r=me :)
<intellectronica> allenap: thanks!
Preview Diff
1 | === modified file 'lib/canonical/launchpad/browser/structuralsubscription.py' | |||
2 | --- lib/canonical/launchpad/browser/structuralsubscription.py 2009-09-17 17:12:58 +0000 | |||
3 | +++ lib/canonical/launchpad/browser/structuralsubscription.py 2009-10-22 09:45:25 +0000 | |||
4 | @@ -4,6 +4,7 @@ | |||
5 | 4 | __metaclass__ = type | 4 | __metaclass__ = type |
6 | 5 | 5 | ||
7 | 6 | __all__ = [ | 6 | __all__ = [ |
8 | 7 | 'StructuralSubscriptionMenuMixin', | ||
9 | 7 | 'StructuralSubscriptionTargetTraversalMixin', | 8 | 'StructuralSubscriptionTargetTraversalMixin', |
10 | 8 | 'StructuralSubscriptionView', | 9 | 'StructuralSubscriptionView', |
11 | 9 | ] | 10 | ] |
12 | @@ -19,13 +20,15 @@ | |||
13 | 19 | from canonical.launchpad.interfaces import ( | 20 | from canonical.launchpad.interfaces import ( |
14 | 20 | BugNotificationLevel, IDistributionSourcePackage, | 21 | BugNotificationLevel, IDistributionSourcePackage, |
15 | 21 | IStructuralSubscriptionForm) | 22 | IStructuralSubscriptionForm) |
16 | 23 | from canonical.launchpad.interfaces.structuralsubscription import ( | ||
17 | 24 | IStructuralSubscriptionTarget) | ||
18 | 22 | from lp.registry.interfaces.person import IPersonSet | 25 | from lp.registry.interfaces.person import IPersonSet |
19 | 23 | from canonical.launchpad.webapp import ( | 26 | from canonical.launchpad.webapp import ( |
20 | 24 | LaunchpadFormView, action, canonical_url, custom_widget, stepthrough) | 27 | LaunchpadFormView, action, canonical_url, custom_widget, stepthrough) |
21 | 25 | from canonical.launchpad.webapp.authorization import check_permission | 28 | from canonical.launchpad.webapp.authorization import check_permission |
22 | 29 | from canonical.launchpad.webapp.menu import Link | ||
23 | 26 | from canonical.widgets import LabeledMultiCheckBoxWidget | 30 | from canonical.widgets import LabeledMultiCheckBoxWidget |
24 | 27 | 31 | ||
25 | 28 | |||
26 | 29 | class StructuralSubscriptionView(LaunchpadFormView): | 32 | class StructuralSubscriptionView(LaunchpadFormView): |
27 | 30 | """View class for structural subscriptions.""" | 33 | """View class for structural subscriptions.""" |
28 | 31 | 34 | ||
29 | @@ -277,3 +280,29 @@ | |||
30 | 277 | """Traverses +subscription portions of URLs.""" | 280 | """Traverses +subscription portions of URLs.""" |
31 | 278 | person = getUtility(IPersonSet).getByName(name) | 281 | person = getUtility(IPersonSet).getByName(name) |
32 | 279 | return self.context.getSubscription(person) | 282 | return self.context.getSubscription(person) |
33 | 283 | |||
34 | 284 | |||
35 | 285 | class StructuralSubscriptionMenuMixin: | ||
36 | 286 | """Mix-in class providing the subscription add/edit menu link.""" | ||
37 | 287 | |||
38 | 288 | def subscribe(self): | ||
39 | 289 | """The subscribe menu link. | ||
40 | 290 | |||
41 | 291 | If the user, or any of the teams he's a member of, already has a | ||
42 | 292 | subscription to the context, the link offer to edit the subscriptions | ||
43 | 293 | and displays the edit icon. Otherwise, the link offers to subscribe | ||
44 | 294 | and displays the add icon. | ||
45 | 295 | """ | ||
46 | 296 | if IStructuralSubscriptionTarget.providedBy(self.context): | ||
47 | 297 | sst = self.context | ||
48 | 298 | else: | ||
49 | 299 | # self.context is a view, and the target is its context | ||
50 | 300 | sst = self.context.context | ||
51 | 301 | |||
52 | 302 | if sst.userHasBugSubscriptions(self.user): | ||
53 | 303 | text = 'Edit bug mail subscription' | ||
54 | 304 | icon = 'edit' | ||
55 | 305 | else: | ||
56 | 306 | text = 'Subscribe to bug mail' | ||
57 | 307 | icon = 'add' | ||
58 | 308 | return Link('+subscribe', text, icon=icon) | ||
59 | 280 | 309 | ||
60 | === modified file 'lib/canonical/launchpad/database/structuralsubscription.py' | |||
61 | --- lib/canonical/launchpad/database/structuralsubscription.py 2009-08-25 12:04:58 +0000 | |||
62 | +++ lib/canonical/launchpad/database/structuralsubscription.py 2009-10-22 09:45:25 +0000 | |||
63 | @@ -326,3 +326,14 @@ | |||
64 | 326 | else: | 326 | else: |
65 | 327 | raise AssertionError( | 327 | raise AssertionError( |
66 | 328 | '%s is not a valid structural subscription target.', self) | 328 | '%s is not a valid structural subscription target.', self) |
67 | 329 | |||
68 | 330 | def userHasBugSubscriptions(self, user): | ||
69 | 331 | """See `IStructuralSubscriptionTarget`.""" | ||
70 | 332 | bug_subscriptions = self.getSubscriptions( | ||
71 | 333 | min_bug_notification_level=BugNotificationLevel.METADATA) | ||
72 | 334 | for subscription in bug_subscriptions: | ||
73 | 335 | if (subscription.subscriber == user or | ||
74 | 336 | user.inTeam(subscription.subscriber)): | ||
75 | 337 | # The user has a bug subscription | ||
76 | 338 | return True | ||
77 | 339 | return False | ||
78 | 329 | 340 | ||
79 | === modified file 'lib/canonical/launchpad/interfaces/structuralsubscription.py' | |||
80 | --- lib/canonical/launchpad/interfaces/structuralsubscription.py 2009-09-09 14:26:18 +0000 | |||
81 | +++ lib/canonical/launchpad/interfaces/structuralsubscription.py 2009-10-22 09:45:25 +0000 | |||
82 | @@ -240,6 +240,9 @@ | |||
83 | 240 | 240 | ||
84 | 241 | target_type_display = Attribute("The type of the target, for display.") | 241 | target_type_display = Attribute("The type of the target, for display.") |
85 | 242 | 242 | ||
86 | 243 | def userHasBugSubscriptions(user): | ||
87 | 244 | """Is `user` subscribed, directly or via a team, to bug mail?""" | ||
88 | 245 | |||
89 | 243 | 246 | ||
90 | 244 | class IStructuralSubscriptionForm(Interface): | 247 | class IStructuralSubscriptionForm(Interface): |
91 | 245 | """Schema for the structural subscription form.""" | 248 | """Schema for the structural subscription form.""" |
92 | 246 | 249 | ||
93 | === modified file 'lib/lp/registry/browser/distribution.py' | |||
94 | --- lib/lp/registry/browser/distribution.py 2009-10-15 10:26:57 +0000 | |||
95 | +++ lib/lp/registry/browser/distribution.py 2009-10-22 09:45:25 +0000 | |||
96 | @@ -76,6 +76,7 @@ | |||
97 | 76 | from lp.soyuz.interfaces.publishedpackage import ( | 76 | from lp.soyuz.interfaces.publishedpackage import ( |
98 | 77 | IPublishedPackageSet) | 77 | IPublishedPackageSet) |
99 | 78 | from canonical.launchpad.browser.structuralsubscription import ( | 78 | from canonical.launchpad.browser.structuralsubscription import ( |
100 | 79 | StructuralSubscriptionMenuMixin, | ||
101 | 79 | StructuralSubscriptionTargetTraversalMixin) | 80 | StructuralSubscriptionTargetTraversalMixin) |
102 | 80 | from canonical.launchpad.webapp import ( | 81 | from canonical.launchpad.webapp import ( |
103 | 81 | action, ApplicationMenu, canonical_url, ContextMenu, custom_widget, | 82 | action, ApplicationMenu, canonical_url, ContextMenu, custom_widget, |
104 | @@ -421,7 +422,7 @@ | |||
105 | 421 | return Link('+addseries', text, icon='add') | 422 | return Link('+addseries', text, icon='add') |
106 | 422 | 423 | ||
107 | 423 | 424 | ||
109 | 424 | class DistributionBugsMenu(ApplicationMenu): | 425 | class DistributionBugsMenu(ApplicationMenu, StructuralSubscriptionMenuMixin): |
110 | 425 | 426 | ||
111 | 426 | usedfor = IDistribution | 427 | usedfor = IDistribution |
112 | 427 | facet = 'bugs' | 428 | facet = 'bugs' |
113 | @@ -451,10 +452,6 @@ | |||
114 | 451 | text = 'Report a bug' | 452 | text = 'Report a bug' |
115 | 452 | return Link('+filebug', text, icon='bug') | 453 | return Link('+filebug', text, icon='bug') |
116 | 453 | 454 | ||
117 | 454 | def subscribe(self): | ||
118 | 455 | text = 'Subscribe to bug mail' | ||
119 | 456 | return Link('+subscribe', text, icon='edit') | ||
120 | 457 | |||
121 | 458 | 455 | ||
122 | 459 | class DistributionSpecificationsMenu(NavigationMenu, | 456 | class DistributionSpecificationsMenu(NavigationMenu, |
123 | 460 | HasSpecificationsMenuMixin): | 457 | HasSpecificationsMenuMixin): |
124 | 461 | 458 | ||
125 | === modified file 'lib/lp/registry/browser/distroseries.py' | |||
126 | --- lib/lp/registry/browser/distroseries.py 2009-09-25 17:00:20 +0000 | |||
127 | +++ lib/lp/registry/browser/distroseries.py 2009-10-22 09:45:25 +0000 | |||
128 | @@ -41,6 +41,7 @@ | |||
129 | 41 | IDistroSeriesLanguageSet) | 41 | IDistroSeriesLanguageSet) |
130 | 42 | from lp.services.worlddata.interfaces.language import ILanguageSet | 42 | from lp.services.worlddata.interfaces.language import ILanguageSet |
131 | 43 | from canonical.launchpad.browser.structuralsubscription import ( | 43 | from canonical.launchpad.browser.structuralsubscription import ( |
132 | 44 | StructuralSubscriptionMenuMixin, | ||
133 | 44 | StructuralSubscriptionTargetTraversalMixin) | 45 | StructuralSubscriptionTargetTraversalMixin) |
134 | 45 | from canonical.launchpad.interfaces.launchpad import ( | 46 | from canonical.launchpad.interfaces.launchpad import ( |
135 | 46 | ILaunchBag, NotFoundError) | 47 | ILaunchBag, NotFoundError) |
136 | @@ -142,7 +143,8 @@ | |||
137 | 142 | 'translations'] | 143 | 'translations'] |
138 | 143 | 144 | ||
139 | 144 | 145 | ||
141 | 145 | class DistroSeriesOverviewMenu(ApplicationMenu): | 146 | class DistroSeriesOverviewMenu( |
142 | 147 | ApplicationMenu, StructuralSubscriptionMenuMixin): | ||
143 | 146 | 148 | ||
144 | 147 | usedfor = IDistroSeries | 149 | usedfor = IDistroSeries |
145 | 148 | facet = 'overview' | 150 | facet = 'overview' |
146 | @@ -202,12 +204,8 @@ | |||
147 | 202 | text = 'Show uploads' | 204 | text = 'Show uploads' |
148 | 203 | return Link('+queue', text, icon='info') | 205 | return Link('+queue', text, icon='info') |
149 | 204 | 206 | ||
156 | 205 | def subscribe(self): | 207 | |
157 | 206 | text = 'Subscribe to bug mail' | 208 | class DistroSeriesBugsMenu(ApplicationMenu, StructuralSubscriptionMenuMixin): |
152 | 207 | return Link('+subscribe', text, icon='edit') | ||
153 | 208 | |||
154 | 209 | |||
155 | 210 | class DistroSeriesBugsMenu(ApplicationMenu): | ||
158 | 211 | 209 | ||
159 | 212 | usedfor = IDistroSeries | 210 | usedfor = IDistroSeries |
160 | 213 | facet = 'bugs' | 211 | facet = 'bugs' |
161 | @@ -223,9 +221,6 @@ | |||
162 | 223 | def nominations(self): | 221 | def nominations(self): |
163 | 224 | return Link('+nominations', 'Review nominations', icon='bug') | 222 | return Link('+nominations', 'Review nominations', icon='bug') |
164 | 225 | 223 | ||
165 | 226 | def subscribe(self): | ||
166 | 227 | return Link('+subscribe', 'Subscribe to bug mail') | ||
167 | 228 | |||
168 | 229 | 224 | ||
169 | 230 | class DistroSeriesSpecificationsMenu(NavigationMenu, | 225 | class DistroSeriesSpecificationsMenu(NavigationMenu, |
170 | 231 | HasSpecificationsMenuMixin): | 226 | HasSpecificationsMenuMixin): |
171 | 232 | 227 | ||
172 | === modified file 'lib/lp/registry/browser/milestone.py' | |||
173 | --- lib/lp/registry/browser/milestone.py 2009-09-22 16:24:23 +0000 | |||
174 | +++ lib/lp/registry/browser/milestone.py 2009-10-22 09:45:25 +0000 | |||
175 | @@ -33,6 +33,7 @@ | |||
176 | 33 | IMilestone, IMilestoneSet, IProjectMilestone) | 33 | IMilestone, IMilestoneSet, IProjectMilestone) |
177 | 34 | from lp.registry.interfaces.product import IProduct | 34 | from lp.registry.interfaces.product import IProduct |
178 | 35 | from canonical.launchpad.browser.structuralsubscription import ( | 35 | from canonical.launchpad.browser.structuralsubscription import ( |
179 | 36 | StructuralSubscriptionMenuMixin, | ||
180 | 36 | StructuralSubscriptionTargetTraversalMixin) | 37 | StructuralSubscriptionTargetTraversalMixin) |
181 | 37 | from canonical.launchpad.webapp import ( | 38 | from canonical.launchpad.webapp import ( |
182 | 38 | action, canonical_url, custom_widget, | 39 | action, canonical_url, custom_widget, |
183 | @@ -58,7 +59,7 @@ | |||
184 | 58 | usedfor = IMilestone | 59 | usedfor = IMilestone |
185 | 59 | 60 | ||
186 | 60 | 61 | ||
188 | 61 | class MilestoneLinkMixin: | 62 | class MilestoneLinkMixin(StructuralSubscriptionMenuMixin): |
189 | 62 | """The menu for this milestone.""" | 63 | """The menu for this milestone.""" |
190 | 63 | 64 | ||
191 | 64 | @enabled_with_permission('launchpad.Edit') | 65 | @enabled_with_permission('launchpad.Edit') |
192 | @@ -72,11 +73,6 @@ | |||
193 | 72 | return Link( | 73 | return Link( |
194 | 73 | '+edit', text, icon='edit', summary=summary, enabled=enabled) | 74 | '+edit', text, icon='edit', summary=summary, enabled=enabled) |
195 | 74 | 75 | ||
196 | 75 | def subscribe(self): | ||
197 | 76 | """The link to subscribe to bug mail.""" | ||
198 | 77 | enabled = not IProjectMilestone.providedBy(self.context) | ||
199 | 78 | return Link('+subscribe', 'Subscribe to bug mail', | ||
200 | 79 | icon='edit', enabled=enabled) | ||
201 | 80 | 76 | ||
202 | 81 | @enabled_with_permission('launchpad.Edit') | 77 | @enabled_with_permission('launchpad.Edit') |
203 | 82 | def create_release(self): | 78 | def create_release(self): |
204 | 83 | 79 | ||
205 | === modified file 'lib/lp/registry/browser/product.py' | |||
206 | --- lib/lp/registry/browser/product.py 2009-10-20 16:45:23 +0000 | |||
207 | +++ lib/lp/registry/browser/product.py 2009-10-22 09:45:25 +0000 | |||
208 | @@ -89,6 +89,7 @@ | |||
209 | 89 | from lp.answers.browser.questiontarget import ( | 89 | from lp.answers.browser.questiontarget import ( |
210 | 90 | QuestionTargetFacetMixin, QuestionTargetTraversalMixin) | 90 | QuestionTargetFacetMixin, QuestionTargetTraversalMixin) |
211 | 91 | from canonical.launchpad.browser.structuralsubscription import ( | 91 | from canonical.launchpad.browser.structuralsubscription import ( |
212 | 92 | StructuralSubscriptionMenuMixin, | ||
213 | 92 | StructuralSubscriptionTargetTraversalMixin) | 93 | StructuralSubscriptionTargetTraversalMixin) |
214 | 93 | from canonical.launchpad.mail import format_address, simple_sendmail | 94 | from canonical.launchpad.mail import format_address, simple_sendmail |
215 | 94 | from canonical.launchpad.webapp import ( | 95 | from canonical.launchpad.webapp import ( |
216 | @@ -311,7 +312,7 @@ | |||
217 | 311 | return Link('+branchvisibility', text) | 312 | return Link('+branchvisibility', text) |
218 | 312 | 313 | ||
219 | 313 | 314 | ||
221 | 314 | class ProductEditLinksMixin: | 315 | class ProductEditLinksMixin(StructuralSubscriptionMenuMixin): |
222 | 315 | """A mixin class for menus that need Product edit links.""" | 316 | """A mixin class for menus that need Product edit links.""" |
223 | 316 | 317 | ||
224 | 317 | @enabled_with_permission('launchpad.Edit') | 318 | @enabled_with_permission('launchpad.Edit') |
225 | @@ -339,10 +340,6 @@ | |||
226 | 339 | text = 'Administer' | 340 | text = 'Administer' |
227 | 340 | return Link('+admin', text, icon='edit') | 341 | return Link('+admin', text, icon='edit') |
228 | 341 | 342 | ||
229 | 342 | def subscribe(self): | ||
230 | 343 | text = 'Subscribe to bug mail' | ||
231 | 344 | return Link('+subscribe', text, icon='edit') | ||
232 | 345 | |||
233 | 346 | 343 | ||
234 | 347 | class IProductEditMenu(Interface): | 344 | class IProductEditMenu(Interface): |
235 | 348 | """A marker interface for the 'Change details' navigation menu.""" | 345 | """A marker interface for the 'Change details' navigation menu.""" |
236 | @@ -431,7 +428,7 @@ | |||
237 | 431 | return Link('+branchvisibility', text, icon='edit') | 428 | return Link('+branchvisibility', text, icon='edit') |
238 | 432 | 429 | ||
239 | 433 | 430 | ||
241 | 434 | class ProductBugsMenu(ApplicationMenu): | 431 | class ProductBugsMenu(ApplicationMenu, StructuralSubscriptionMenuMixin): |
242 | 435 | 432 | ||
243 | 436 | usedfor = IProduct | 433 | usedfor = IProduct |
244 | 437 | facet = 'bugs' | 434 | facet = 'bugs' |
245 | @@ -460,10 +457,6 @@ | |||
246 | 460 | text = 'Change security contact' | 457 | text = 'Change security contact' |
247 | 461 | return Link('+securitycontact', text, icon='edit') | 458 | return Link('+securitycontact', text, icon='edit') |
248 | 462 | 459 | ||
249 | 463 | def subscribe(self): | ||
250 | 464 | text = 'Subscribe to bug mail' | ||
251 | 465 | return Link('+subscribe', text, icon='edit') | ||
252 | 466 | |||
253 | 467 | 460 | ||
254 | 468 | class ProductSpecificationsMenu(NavigationMenu, | 461 | class ProductSpecificationsMenu(NavigationMenu, |
255 | 469 | HasSpecificationsMenuMixin): | 462 | HasSpecificationsMenuMixin): |
256 | 470 | 463 | ||
257 | === modified file 'lib/lp/registry/browser/productseries.py' | |||
258 | --- lib/lp/registry/browser/productseries.py 2009-10-22 07:23:57 +0000 | |||
259 | +++ lib/lp/registry/browser/productseries.py 2009-10-22 09:45:25 +0000 | |||
260 | @@ -54,6 +54,7 @@ | |||
261 | 54 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities | 54 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
262 | 55 | from lp.registry.browser import StatusCount | 55 | from lp.registry.browser import StatusCount |
263 | 56 | from canonical.launchpad.browser.structuralsubscription import ( | 56 | from canonical.launchpad.browser.structuralsubscription import ( |
264 | 57 | StructuralSubscriptionMenuMixin, | ||
265 | 57 | StructuralSubscriptionTargetTraversalMixin) | 58 | StructuralSubscriptionTargetTraversalMixin) |
266 | 58 | from lp.translations.interfaces.potemplate import IPOTemplateSet | 59 | from lp.translations.interfaces.potemplate import IPOTemplateSet |
267 | 59 | from lp.translations.interfaces.productserieslanguage import ( | 60 | from lp.translations.interfaces.productserieslanguage import ( |
268 | @@ -148,7 +149,8 @@ | |||
269 | 148 | return Link(link, text, summary=summary) | 149 | return Link(link, text, summary=summary) |
270 | 149 | 150 | ||
271 | 150 | 151 | ||
273 | 151 | class ProductSeriesOverviewMenu(ApplicationMenu): | 152 | class ProductSeriesOverviewMenu( |
274 | 153 | ApplicationMenu, StructuralSubscriptionMenuMixin): | ||
275 | 152 | """The overview menu.""" | 154 | """The overview menu.""" |
276 | 153 | usedfor = IProductSeries | 155 | usedfor = IProductSeries |
277 | 154 | facet = 'overview' | 156 | facet = 'overview' |
278 | @@ -219,13 +221,8 @@ | |||
279 | 219 | text = 'Download RDF metadata' | 221 | text = 'Download RDF metadata' |
280 | 220 | return Link('+rdf', text, icon='download') | 222 | return Link('+rdf', text, icon='download') |
281 | 221 | 223 | ||
289 | 222 | def subscribe(self): | 224 | |
290 | 223 | """Return a link to subscribe to bug mail.""" | 225 | class ProductSeriesBugsMenu(ApplicationMenu, StructuralSubscriptionMenuMixin): |
284 | 224 | text = 'Subscribe to bug mail' | ||
285 | 225 | return Link('+subscribe', text, icon='edit') | ||
286 | 226 | |||
287 | 227 | |||
288 | 228 | class ProductSeriesBugsMenu(ApplicationMenu): | ||
291 | 229 | """The bugs menu.""" | 226 | """The bugs menu.""" |
292 | 230 | usedfor = IProductSeries | 227 | usedfor = IProductSeries |
293 | 231 | facet = 'bugs' | 228 | facet = 'bugs' |
294 | @@ -243,10 +240,6 @@ | |||
295 | 243 | """Return a link to review bugs nominated for this series.""" | 240 | """Return a link to review bugs nominated for this series.""" |
296 | 244 | return Link('+nominations', 'Review nominations', icon='bug') | 241 | return Link('+nominations', 'Review nominations', icon='bug') |
297 | 245 | 242 | ||
298 | 246 | def subscribe(self): | ||
299 | 247 | """Return a link to subscribe to bug mail.""" | ||
300 | 248 | return Link('+subscribe', 'Subscribe to bug mail') | ||
301 | 249 | |||
302 | 250 | 243 | ||
303 | 251 | class ProductSeriesSpecificationsMenu(NavigationMenu, | 244 | class ProductSeriesSpecificationsMenu(NavigationMenu, |
304 | 252 | HasSpecificationsMenuMixin): | 245 | HasSpecificationsMenuMixin): |
305 | 253 | 246 | ||
306 | === modified file 'lib/lp/registry/configure.zcml' | |||
307 | --- lib/lp/registry/configure.zcml 2009-10-20 13:21:44 +0000 | |||
308 | +++ lib/lp/registry/configure.zcml 2009-10-22 09:45:25 +0000 | |||
309 | @@ -149,7 +149,8 @@ | |||
310 | 149 | getSubscription | 149 | getSubscription |
311 | 150 | getSubscriptions | 150 | getSubscriptions |
312 | 151 | parent_subscription_target | 151 | parent_subscription_target |
314 | 152 | target_type_display"/> | 152 | target_type_display |
315 | 153 | userHasBugSubscriptions"/> | ||
316 | 153 | <require | 154 | <require |
317 | 154 | permission="launchpad.AnyPerson" | 155 | permission="launchpad.AnyPerson" |
318 | 155 | attributes=" | 156 | attributes=" |
319 | @@ -285,7 +286,8 @@ | |||
320 | 285 | getSubscription | 286 | getSubscription |
321 | 286 | getBugNotificationsRecipients | 287 | getBugNotificationsRecipients |
322 | 287 | parent_subscription_target | 288 | parent_subscription_target |
324 | 288 | target_type_display"/> | 289 | target_type_display |
325 | 290 | userHasBugSubscriptions"/> | ||
326 | 289 | <require | 291 | <require |
327 | 290 | permission="launchpad.AnyPerson" | 292 | permission="launchpad.AnyPerson" |
328 | 291 | attributes=" | 293 | attributes=" |
329 | @@ -395,7 +397,8 @@ | |||
330 | 395 | target_type_display | 397 | target_type_display |
331 | 396 | official_bug_tags | 398 | official_bug_tags |
332 | 397 | findRelatedArchives | 399 | findRelatedArchives |
334 | 398 | findRelatedArchivePublications"/> | 400 | findRelatedArchivePublications |
335 | 401 | userHasBugSubscriptions"/> | ||
336 | 399 | <require | 402 | <require |
337 | 400 | permission="launchpad.AnyPerson" | 403 | permission="launchpad.AnyPerson" |
338 | 401 | attributes=" | 404 | attributes=" |
339 | @@ -903,7 +906,8 @@ | |||
340 | 903 | getSubscription | 906 | getSubscription |
341 | 904 | getBugNotificationsRecipients | 907 | getBugNotificationsRecipients |
342 | 905 | parent_subscription_target | 908 | parent_subscription_target |
344 | 906 | target_type_display"/> | 909 | target_type_display |
345 | 910 | userHasBugSubscriptions"/> | ||
346 | 907 | <require | 911 | <require |
347 | 908 | permission="launchpad.AnyPerson" | 912 | permission="launchpad.AnyPerson" |
348 | 909 | attributes=" | 913 | attributes=" |
349 | @@ -1150,7 +1154,8 @@ | |||
350 | 1150 | getSubscription | 1154 | getSubscription |
351 | 1151 | getBugNotificationsRecipients | 1155 | getBugNotificationsRecipients |
352 | 1152 | parent_subscription_target | 1156 | parent_subscription_target |
354 | 1153 | target_type_display"/> | 1157 | target_type_display |
355 | 1158 | userHasBugSubscriptions"/> | ||
356 | 1154 | <require | 1159 | <require |
357 | 1155 | permission="launchpad.AnyPerson" | 1160 | permission="launchpad.AnyPerson" |
358 | 1156 | attributes=" | 1161 | attributes=" |
359 | @@ -1297,7 +1302,8 @@ | |||
360 | 1297 | getSubscription | 1302 | getSubscription |
361 | 1298 | getSubscriptions | 1303 | getSubscriptions |
362 | 1299 | parent_subscription_target | 1304 | parent_subscription_target |
364 | 1300 | target_type_display"/> | 1305 | target_type_display |
365 | 1306 | userHasBugSubscriptions"/> | ||
366 | 1301 | <require | 1307 | <require |
367 | 1302 | permission="launchpad.AnyPerson" | 1308 | permission="launchpad.AnyPerson" |
368 | 1303 | attributes=" | 1309 | attributes=" |
369 | @@ -1407,7 +1413,8 @@ | |||
370 | 1407 | getSubscription | 1413 | getSubscription |
371 | 1408 | getBugNotificationsRecipients | 1414 | getBugNotificationsRecipients |
372 | 1409 | parent_subscription_target | 1415 | parent_subscription_target |
374 | 1410 | target_type_display"/> | 1416 | target_type_display |
375 | 1417 | userHasBugSubscriptions"/> | ||
376 | 1411 | <require | 1418 | <require |
377 | 1412 | permission="launchpad.AnyPerson" | 1419 | permission="launchpad.AnyPerson" |
378 | 1413 | attributes=" | 1420 | attributes=" |
379 | 1414 | 1421 | ||
380 | === modified file 'lib/lp/registry/model/milestone.py' | |||
381 | --- lib/lp/registry/model/milestone.py 2009-09-29 18:55:25 +0000 | |||
382 | +++ lib/lp/registry/model/milestone.py 2009-10-22 09:45:25 +0000 | |||
383 | @@ -301,3 +301,7 @@ | |||
384 | 301 | def official_bug_tags(self): | 301 | def official_bug_tags(self): |
385 | 302 | """See `IHasBugs`.""" | 302 | """See `IHasBugs`.""" |
386 | 303 | return self.target.official_bug_tags | 303 | return self.target.official_bug_tags |
387 | 304 | |||
388 | 305 | def userHasBugSubscriptions(self, user): | ||
389 | 306 | """See `IStructuralSubscriptionTarget`.""" | ||
390 | 307 | return False |
This branch makes the menu link for subscribing to bug mail sensitive to the user's existing subsription. If the user is already subscribed (directly, or via one of the teams he's a member of), the link displays the edit icon and offers to edit subscription. If no subscription exists the link offers to subscribe and displays the add icon.
There are no lint warnings related to my changes (the usual lazr stuff is still there).
To test, run bin/test -vv -t xx-milestone- add-and- edit.txt -t xx-distroseries -index. txt -t xx-productserie s-index. txt -t xx-productserie s-add-and- edit.txt -t xx-bug- subscriptions. txt -t xx-distribution -bugs-page. txt -t xx-distroreleas e-bugs- page.txt -t xx-product- bugs-page. txt > test-output.txt
To play with the UI, go to https:/ /bugs.launchpad .dev/jokosher or https:/ /bugs.launchpad .dev/ubuntu or any of the other structural subscription targets and try to subscribe and unsubscribe.
While working on this I realised that the form for editing structural subscriptions does not behave in the expected way, like all other boomerang forms. It doesn't offer a cancel link, and after submitting it displays the form again, instead of returning to the context. I plan to fix that in a future branch.