Merge lp:~stevenk/launchpad/less-powerful-bug-supervisors into lp:launchpad
- less-powerful-bug-supervisors
- Merge into devel
Proposed by
Steve Kowalik
Status: | Merged |
---|---|
Approved by: | Steve Kowalik |
Approved revision: | no longer in the source branch. |
Merged at revision: | 14341 |
Proposed branch: | lp:~stevenk/launchpad/less-powerful-bug-supervisors |
Merge into: | lp:launchpad |
Diff against target: |
497 lines (+140/-100) 10 files modified
lib/lp/bugs/browser/bugtarget.py (+4/-1) lib/lp/bugs/browser/bugtask.py (+28/-42) lib/lp/bugs/browser/tests/test_bugtask.py (+1/-1) lib/lp/bugs/configure.zcml (+1/-2) lib/lp/bugs/interfaces/bugtask.py (+5/-4) lib/lp/bugs/model/bugtask.py (+30/-42) lib/lp/bugs/model/tests/test_bugtask.py (+65/-0) lib/lp/bugs/stories/bugtask-management/xx-bugtask-edit-forms.txt (+2/-4) lib/lp/bugs/templates/bugtask-edit-form.pt (+2/-2) lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt (+2/-2) |
To merge this branch: | bzr merge lp:~stevenk/launchpad/less-powerful-bug-supervisors |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ian Booth (community) | code | Approve | |
Review via email: mp+82632@code.launchpad.net |
Commit message
[r=wallyworld][bug=885692] Consolidate who has access to change details on bugtasks.
Description of the change
This branch is somewhat misnamed. It is not about making bug supervisors less powerful or taking away abilities they have today, it is about making sure that pillar owners, drivers and admins have the same rights, so make sure everyone has the same level of power.
The only place I've not fixed is the browser code inside bugtarget, which is a little difficult and requires thought since we don't actually have a bugtask yet -- but it has been marked with an XXX.
I have discussed this branch with Curtis and William, they like the approach.
I have cleaned up some lint that I noticed.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'lib/lp/bugs/browser/bugtarget.py' |
2 | --- lib/lp/bugs/browser/bugtarget.py 2011-07-28 17:31:16 +0000 |
3 | +++ lib/lp/bugs/browser/bugtarget.py 2011-11-21 00:29:30 +0000 |
4 | @@ -131,7 +131,6 @@ |
5 | from lp.bugs.interfaces.bugtracker import IBugTracker |
6 | from lp.bugs.interfaces.malone import IMaloneApplication |
7 | from lp.bugs.interfaces.securitycontact import IHasSecurityContact |
8 | -from lp.bugs.model.bugtask import BugTask |
9 | from lp.bugs.model.structuralsubscription import ( |
10 | get_structural_subscriptions_for_target, |
11 | ) |
12 | @@ -402,6 +401,8 @@ |
13 | # If the context is a project group we want to render the optional |
14 | # fields since they will initially be hidden and later exposed if the |
15 | # selected project supports them. |
16 | + # XXX: StevenK 2011-11-18 bug=885692 This should make use of |
17 | + # IBugTask.userHasPrivileges(). |
18 | include_extra_fields = IProjectGroup.providedBy(context) |
19 | if not include_extra_fields and IHasBugSupervisor.providedBy(context): |
20 | include_extra_fields = self.user.inTeam(context.bug_supervisor) |
21 | @@ -627,6 +628,8 @@ |
22 | if extra_data.private: |
23 | params.private = extra_data.private |
24 | |
25 | + # XXX: StevenK 2011-11-18 bug=885692 This should make use of |
26 | + # IBugTask.userHasPrivileges(). |
27 | # Apply any extra options given by a bug supervisor. |
28 | if IHasBugSupervisor.providedBy(context): |
29 | if self.user.inTeam(context.bug_supervisor): |
30 | |
31 | === modified file 'lib/lp/bugs/browser/bugtask.py' |
32 | --- lib/lp/bugs/browser/bugtask.py 2011-11-20 23:50:47 +0000 |
33 | +++ lib/lp/bugs/browser/bugtask.py 2011-11-21 00:29:30 +0000 |
34 | @@ -1281,7 +1281,20 @@ |
35 | } |
36 | |
37 | |
38 | -class BugTaskEditView(LaunchpadEditFormView, BugTaskBugWatchMixin): |
39 | +class BugTaskPrivilegeMixin: |
40 | + |
41 | + @cachedproperty |
42 | + def user_has_privileges(self): |
43 | + """Is the user privileged? That is, an admin, pillar owner, driver |
44 | + or bug supervisor. |
45 | + |
46 | + If yes, return True, otherwise return False. |
47 | + """ |
48 | + return self.context.userHasPrivileges(self.user) |
49 | + |
50 | + |
51 | +class BugTaskEditView(LaunchpadEditFormView, BugTaskBugWatchMixin, |
52 | + BugTaskPrivilegeMixin): |
53 | """The view class used for the task +editstatus page.""" |
54 | |
55 | schema = IBugTask |
56 | @@ -1346,26 +1359,24 @@ |
57 | |
58 | # XXX: Brad Bollenbach 2006-09-29 bug=63000: Permission checking |
59 | # doesn't belong here! |
60 | - if ('milestone' in editable_field_names and |
61 | - not self.userCanEditMilestone()): |
62 | - editable_field_names.remove("milestone") |
63 | - |
64 | - if ('importance' in editable_field_names and |
65 | - not self.userCanEditImportance()): |
66 | - editable_field_names.remove("importance") |
67 | + if not self.user_has_privileges: |
68 | + if 'milestone' in editable_field_names: |
69 | + editable_field_names.remove("milestone") |
70 | + if 'importance' in editable_field_names: |
71 | + editable_field_names.remove("importance") |
72 | else: |
73 | editable_field_names = set(('bugwatch', )) |
74 | if self.context.bugwatch is None: |
75 | editable_field_names.update(('status', 'assignee')) |
76 | if ('importance' in self.default_field_names |
77 | - and self.userCanEditImportance()): |
78 | + and self.user_has_privileges): |
79 | editable_field_names.add('importance') |
80 | else: |
81 | bugtracker = self.context.bugwatch.bugtracker |
82 | if bugtracker.bugtrackertype == BugTrackerType.EMAILADDRESS: |
83 | editable_field_names.add('status') |
84 | if ('importance' in self.default_field_names |
85 | - and self.userCanEditImportance()): |
86 | + and self.user_has_privileges): |
87 | editable_field_names.add('importance') |
88 | |
89 | if self.show_target_widget: |
90 | @@ -1519,10 +1530,8 @@ |
91 | if self.context.target_uses_malone: |
92 | read_only_field_names = [] |
93 | |
94 | - if not self.userCanEditMilestone(): |
95 | + if not self.user_has_privileges: |
96 | read_only_field_names.append("milestone") |
97 | - |
98 | - if not self.userCanEditImportance(): |
99 | read_only_field_names.append("importance") |
100 | else: |
101 | editable_field_names = self.editable_field_names |
102 | @@ -1532,20 +1541,6 @@ |
103 | |
104 | return read_only_field_names |
105 | |
106 | - def userCanEditMilestone(self): |
107 | - """Can the user edit the Milestone field? |
108 | - |
109 | - If yes, return True, otherwise return False. |
110 | - """ |
111 | - return self.context.userCanEditMilestone(self.user) |
112 | - |
113 | - def userCanEditImportance(self): |
114 | - """Can the user edit the Importance field? |
115 | - |
116 | - If yes, return True, otherwise return False. |
117 | - """ |
118 | - return self.context.userCanEditImportance(self.user) |
119 | - |
120 | def validate(self, data): |
121 | if self.show_sourcepackagename_widget and 'sourcepackagename' in data: |
122 | data['target'] = self.context.distroseries |
123 | @@ -3776,7 +3771,8 @@ |
124 | return True |
125 | |
126 | |
127 | -class BugTaskTableRowView(LaunchpadView, BugTaskBugWatchMixin): |
128 | +class BugTaskTableRowView(LaunchpadView, BugTaskBugWatchMixin, |
129 | + BugTaskPrivilegeMixin): |
130 | """Browser class for rendering a bugtask row on the bug page.""" |
131 | |
132 | is_conjoined_slave = None |
133 | @@ -3824,7 +3820,7 @@ |
134 | target_link_title=self.target_link_title, |
135 | user_can_delete=self.user_can_delete_bugtask, |
136 | delete_link=delete_link, |
137 | - user_can_edit_importance=self.user_can_edit_importance, |
138 | + user_can_edit_importance=self.user_has_privileges, |
139 | importance_css_class='importance' + self.context.importance.name, |
140 | importance_title=self.context.importance.title, |
141 | # We always look up all milestones, so there's no harm |
142 | @@ -3963,9 +3959,7 @@ |
143 | |
144 | If yes, return True, otherwise return False. |
145 | """ |
146 | - bugtask = self.context |
147 | - return (self.user_can_edit_status |
148 | - and bugtask.userCanEditImportance(self.user)) |
149 | + return self.user_can_edit_status and self.user_has_privileges |
150 | |
151 | @cachedproperty |
152 | def user_can_edit_status(self): |
153 | @@ -3990,14 +3984,6 @@ |
154 | return self.user is not None |
155 | |
156 | @cachedproperty |
157 | - def user_can_edit_milestone(self): |
158 | - """Can the user edit the Milestone field? |
159 | - |
160 | - If yes, return True, otherwise return False. |
161 | - """ |
162 | - return self.context.userCanEditMilestone(self.user) |
163 | - |
164 | - @cachedproperty |
165 | def user_can_delete_bugtask(self): |
166 | """Can the user delete the bug task? |
167 | |
168 | @@ -4071,9 +4057,9 @@ |
169 | request=self.api_request) |
170 | if cx.milestone else None), |
171 | user_can_edit_assignee=self.user_can_edit_assignee, |
172 | - user_can_edit_milestone=self.user_can_edit_milestone, |
173 | + user_can_edit_milestone=self.user_has_privileges, |
174 | user_can_edit_status=self.user_can_edit_status, |
175 | - user_can_edit_importance=self.user_can_edit_importance, |
176 | + user_can_edit_importance=self.user_has_privileges, |
177 | ) |
178 | |
179 | |
180 | |
181 | === modified file 'lib/lp/bugs/browser/tests/test_bugtask.py' |
182 | --- lib/lp/bugs/browser/tests/test_bugtask.py 2011-11-19 05:44:39 +0000 |
183 | +++ lib/lp/bugs/browser/tests/test_bugtask.py 2011-11-21 00:29:30 +0000 |
184 | @@ -185,7 +185,7 @@ |
185 | self.invalidate_caches(bug.default_bugtask) |
186 | self.getUserBrowser(url, owner) |
187 | # At least 20 of these should be removed. |
188 | - self.assertThat(recorder, HasQueryCount(LessThan(100))) |
189 | + self.assertThat(recorder, HasQueryCount(LessThan(101))) |
190 | count_with_no_branches = recorder.count |
191 | for sp in sourcepackages: |
192 | self.makeLinkedBranchMergeProposal(sp, bug, owner) |
193 | |
194 | === modified file 'lib/lp/bugs/configure.zcml' |
195 | --- lib/lp/bugs/configure.zcml 2011-11-16 07:25:58 +0000 |
196 | +++ lib/lp/bugs/configure.zcml 2011-11-21 00:29:30 +0000 |
197 | @@ -199,8 +199,7 @@ |
198 | isSubscribed |
199 | getPackageComponent |
200 | maybeConfirm |
201 | - userCanEditImportance |
202 | - userCanEditMilestone |
203 | + userHasPrivileges |
204 | userCanSetAnyAssignee |
205 | userCanUnassign"/> |
206 | <require |
207 | |
208 | === modified file 'lib/lp/bugs/interfaces/bugtask.py' |
209 | --- lib/lp/bugs/interfaces/bugtask.py 2011-11-04 18:55:42 +0000 |
210 | +++ lib/lp/bugs/interfaces/bugtask.py 2011-11-21 00:29:30 +0000 |
211 | @@ -913,11 +913,12 @@ |
212 | not a package task, returns None. |
213 | """ |
214 | |
215 | - def userCanEditMilestone(user): |
216 | - """Can the user edit the Milestone field?""" |
217 | + def userHasPrivileges(user): |
218 | + """Is the user a privledged one, allowed to changed details on a |
219 | + bug?. |
220 | |
221 | - def userCanEditImportance(user): |
222 | - """Can the user edit the Importance field?""" |
223 | + :return: A boolean. |
224 | + """ |
225 | |
226 | |
227 | # Set schemas that were impossible to specify during the definition of |
228 | |
229 | === modified file 'lib/lp/bugs/model/bugtask.py' |
230 | --- lib/lp/bugs/model/bugtask.py 2011-11-19 05:44:39 +0000 |
231 | +++ lib/lp/bugs/model/bugtask.py 2011-11-21 00:29:30 +0000 |
232 | @@ -155,6 +155,7 @@ |
233 | from lp.registry.interfaces.product import IProduct |
234 | from lp.registry.interfaces.productseries import IProductSeries |
235 | from lp.registry.interfaces.projectgroup import IProjectGroup |
236 | +from lp.registry.interfaces.role import IPersonRoles |
237 | from lp.registry.interfaces.sourcepackage import ISourcePackage |
238 | from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet |
239 | from lp.registry.model.pillar import pillar_sort_key |
240 | @@ -835,7 +836,7 @@ |
241 | |
242 | def transitionToMilestone(self, new_milestone, user): |
243 | """See `IBugTask`.""" |
244 | - if not self.userCanEditMilestone(user): |
245 | + if not self.userHasPrivileges(user): |
246 | raise UserCannotEditBugTaskMilestone( |
247 | "User does not have sufficient permissions " |
248 | "to edit the bug task milestone.") |
249 | @@ -844,7 +845,7 @@ |
250 | |
251 | def transitionToImportance(self, new_importance, user): |
252 | """See `IBugTask`.""" |
253 | - if not self.userCanEditImportance(user): |
254 | + if not self.userHasPrivileges(user): |
255 | raise UserCannotEditBugTaskImportance( |
256 | "User does not have sufficient permissions " |
257 | "to edit the bug task importance.") |
258 | @@ -912,15 +913,10 @@ |
259 | def canTransitionToStatus(self, new_status, user): |
260 | """See `IBugTask`.""" |
261 | new_status = normalize_bugtask_status(new_status) |
262 | - celebrities = getUtility(ILaunchpadCelebrities) |
263 | if (self.status == BugTaskStatus.FIXRELEASED and |
264 | (user.id == self.bug.ownerID or user.inTeam(self.bug.owner))): |
265 | return True |
266 | - elif (user.inTeam(self.pillar.bug_supervisor) or |
267 | - user.inTeam(self.pillar.owner) or |
268 | - user.id == celebrities.bug_watch_updater.id or |
269 | - user.id == celebrities.bug_importer.id or |
270 | - user.id == celebrities.janitor.id): |
271 | + elif self.userHasPrivileges(user): |
272 | return True |
273 | else: |
274 | return (self.status not in ( |
275 | @@ -1065,20 +1061,6 @@ |
276 | if new_status < BugTaskStatus.FIXRELEASED: |
277 | self.date_fix_released = None |
278 | |
279 | - def _userCanSetAssignee(self, user): |
280 | - """Used by methods to check if user can assign or unassign bugtask.""" |
281 | - celebrities = getUtility(ILaunchpadCelebrities) |
282 | - return ( |
283 | - user.inTeam(self.pillar.bug_supervisor) or |
284 | - user.inTeam(self.pillar.owner) or |
285 | - user.inTeam(self.pillar.driver) or |
286 | - (self.distroseries is not None and |
287 | - user.inTeam(self.distroseries.driver)) or |
288 | - (self.productseries is not None and |
289 | - user.inTeam(self.productseries.driver)) or |
290 | - user.inTeam(celebrities.admin) |
291 | - or user == celebrities.bug_importer) |
292 | - |
293 | def userCanSetAnyAssignee(self, user): |
294 | """See `IBugTask`.""" |
295 | if user is None: |
296 | @@ -1086,7 +1068,7 @@ |
297 | elif self.pillar.bug_supervisor is None: |
298 | return True |
299 | else: |
300 | - return self._userCanSetAssignee(user) |
301 | + return self.userHasPrivileges(user) |
302 | |
303 | def userCanUnassign(self, user): |
304 | """True if user can set the assignee to None. |
305 | @@ -1096,7 +1078,7 @@ |
306 | Launchpad admins can always unassign. |
307 | """ |
308 | return user is not None and ( |
309 | - user.inTeam(self.assignee) or self._userCanSetAssignee(user)) |
310 | + user.inTeam(self.assignee) or self.userHasPrivileges(user)) |
311 | |
312 | def canTransitionToAssignee(self, assignee): |
313 | """See `IBugTask`.""" |
314 | @@ -1364,25 +1346,31 @@ |
315 | else: |
316 | return None |
317 | |
318 | - def _userIsPillarEditor(self, user): |
319 | - """Can the user edit this tasks's pillar?""" |
320 | - if user is None: |
321 | + def userHasPrivileges(self, user): |
322 | + """See `IBugTask`.""" |
323 | + if not user: |
324 | return False |
325 | - pillar = self.pillar |
326 | - return ((pillar.bug_supervisor is not None and |
327 | - user.inTeam(pillar.bug_supervisor)) or |
328 | - pillar.userCanEdit(user)) |
329 | - |
330 | - def userCanEditMilestone(self, user): |
331 | - """See `IBugTask`.""" |
332 | - return self._userIsPillarEditor(user) |
333 | - |
334 | - def userCanEditImportance(self, user): |
335 | - """See `IBugTask`.""" |
336 | - celebs = getUtility(ILaunchpadCelebrities) |
337 | - return (self._userIsPillarEditor(user) or |
338 | - user == celebs.bug_watch_updater or |
339 | - user == celebs.bug_importer) |
340 | + role = IPersonRoles(user) |
341 | + # Admins can always change bug details. |
342 | + if role.in_admin: |
343 | + return True |
344 | + |
345 | + # Similar to admins, the Bug Watch Updater, Bug Importer and |
346 | + # Janitor can always change bug details. |
347 | + if ( |
348 | + role.in_bug_watch_updater or role.in_bug_importer or |
349 | + role.in_janitor): |
350 | + return True |
351 | + |
352 | + # Otherwise, if you're a member of the pillar owner, drivers, or the |
353 | + # bug supervisor, you can change bug details. |
354 | + return ( |
355 | + role.isOwner(self.pillar) or role.isOneOfDrivers(self.pillar) or |
356 | + role.isBugSupervisor(self.pillar) or |
357 | + (self.distroseries is not None and |
358 | + role.isDriver(self.distroseries)) or |
359 | + (self.productseries is not None and |
360 | + role.isDriver(self.productseries))) |
361 | |
362 | def __repr__(self): |
363 | return "<BugTask for bug %s on %r>" % (self.bugID, self.target) |
364 | |
365 | === modified file 'lib/lp/bugs/model/tests/test_bugtask.py' |
366 | --- lib/lp/bugs/model/tests/test_bugtask.py 2011-11-14 12:23:59 +0000 |
367 | +++ lib/lp/bugs/model/tests/test_bugtask.py 2011-11-21 00:29:30 +0000 |
368 | @@ -2590,3 +2590,68 @@ |
369 | # Check the delete really worked. |
370 | with person_logged_in(removeSecurityProxy(db_bug).owner): |
371 | self.assertEqual([db_bug.default_bugtask], db_bug.bugtasks) |
372 | + |
373 | + |
374 | +class TestBugTaskUserHasPriviliges(TestCaseWithFactory): |
375 | + |
376 | + layer = DatabaseFunctionalLayer |
377 | + |
378 | + def setUp(self): |
379 | + super(TestBugTaskUserHasPriviliges, self).setUp() |
380 | + self.celebrities = getUtility(ILaunchpadCelebrities) |
381 | + |
382 | + def test_admin_is_allowed(self): |
383 | + # An admin always has privileges. |
384 | + bugtask = self.factory.makeBugTask() |
385 | + self.assertTrue(bugtask.userHasPrivileges(self.celebrities.admin)) |
386 | + |
387 | + def test_bug_celebrities_are_allowed(self): |
388 | + # The three bug celebrities (bug watcher, bug importer and |
389 | + # janitor always have privileges. |
390 | + bugtask = self.factory.makeBugTask() |
391 | + for celeb in ( |
392 | + self.celebrities.bug_watch_updater, |
393 | + self.celebrities.bug_importer, self.celebrities.janitor): |
394 | + self.assertTrue(bugtask.userHasPrivileges(celeb)) |
395 | + |
396 | + def test_pillar_owner_is_allowed(self): |
397 | + # The pillar owner has privileges. |
398 | + pillar = self.factory.makeProduct() |
399 | + bugtask = self.factory.makeBugTask(target=pillar) |
400 | + self.assertTrue(bugtask.userHasPrivileges(pillar.owner)) |
401 | + |
402 | + def test_pillar_driver_is_allowed(self): |
403 | + # The pillar driver has privileges. |
404 | + pillar = self.factory.makeProduct() |
405 | + removeSecurityProxy(pillar).driver = self.factory.makePerson() |
406 | + bugtask = self.factory.makeBugTask(target=pillar) |
407 | + self.assertTrue(bugtask.userHasPrivileges(pillar.driver)) |
408 | + |
409 | + def test_pillar_bug_supervisor(self): |
410 | + # The pillar bug supervisor has privileges. |
411 | + pillar = self.factory.makeProduct() |
412 | + bugsupervisor = self.factory.makePerson() |
413 | + removeSecurityProxy(pillar).setBugSupervisor( |
414 | + bugsupervisor, self.celebrities.admin) |
415 | + bugtask = self.factory.makeBugTask(target=pillar) |
416 | + self.assertTrue(bugtask.userHasPrivileges(bugsupervisor)) |
417 | + |
418 | + def test_productseries_driver_is_allowed(self): |
419 | + # The series driver has privileges. |
420 | + series = self.factory.makeProductSeries() |
421 | + removeSecurityProxy(series).driver = self.factory.makePerson() |
422 | + bugtask = self.factory.makeBugTask(target=series) |
423 | + self.assertTrue(bugtask.userHasPrivileges(series.driver)) |
424 | + |
425 | + def test_distroseries_driver_is_allowed(self): |
426 | + # The series driver has privileges. |
427 | + distroseries = self.factory.makeDistroSeries() |
428 | + removeSecurityProxy(distroseries).driver = self.factory.makePerson() |
429 | + bugtask = self.factory.makeBugTask(target=distroseries) |
430 | + self.assertTrue(bugtask.userHasPrivileges(distroseries.driver)) |
431 | + |
432 | + def test_random_has_no_privileges(self): |
433 | + # Joe Random has no privileges. |
434 | + bugtask = self.factory.makeBugTask() |
435 | + self.assertFalse( |
436 | + bugtask.userHasPrivileges(self.factory.makePerson())) |
437 | |
438 | === modified file 'lib/lp/bugs/stories/bugtask-management/xx-bugtask-edit-forms.txt' |
439 | --- lib/lp/bugs/stories/bugtask-management/xx-bugtask-edit-forms.txt 2011-11-14 08:30:52 +0000 |
440 | +++ lib/lp/bugs/stories/bugtask-management/xx-bugtask-edit-forms.txt 2011-11-21 00:29:30 +0000 |
441 | @@ -29,11 +29,9 @@ |
442 | Traceback (most recent call last): |
443 | ... |
444 | LinkNotFoundError |
445 | + |
446 | >>> print admin_browser.getLink('Low', index=1).url |
447 | - Traceback (most recent call last): |
448 | - ... |
449 | - LinkNotFoundError |
450 | - |
451 | + http://bugs.launchpad.dev/debian/+source/mozilla-firefox/+bug/1/+editstatus |
452 | >>> print admin_browser.getLink('New', index=1).url |
453 | http://bugs...dev/ubuntu/+source/mozilla-firefox/+bug/1/+editstatus |
454 | >>> print admin_browser.getLink('Medium').url |
455 | |
456 | === modified file 'lib/lp/bugs/templates/bugtask-edit-form.pt' |
457 | --- lib/lp/bugs/templates/bugtask-edit-form.pt 2011-08-11 06:03:15 +0000 |
458 | +++ lib/lp/bugs/templates/bugtask-edit-form.pt 2011-11-21 00:29:30 +0000 |
459 | @@ -179,14 +179,14 @@ |
460 | <td tal:content="structure view/widgets/status" /> |
461 | <td title="Changeable only by a project maintainer or bug supervisor"> |
462 | <span |
463 | - tal:condition="not:view/userCanEditImportance" |
464 | + tal:condition="not:view/user_has_privileges" |
465 | class="sprite read-only"></span> |
466 | <tal:widget content="structure view/widgets/importance" /> |
467 | </td> |
468 | <td tal:condition="view/widgets/milestone" |
469 | title="Changeable only by a project maintainer or bug supervisor"> |
470 | <span |
471 | - tal:condition="not:view/userCanEditMilestone" |
472 | + tal:condition="not:view/user_has_privileges" |
473 | class="sprite read-only"></span> |
474 | <tal:widget content="structure view/widgets/milestone" /> |
475 | </td> |
476 | |
477 | === modified file 'lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt' |
478 | --- lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt 2011-11-04 18:55:42 +0000 |
479 | +++ lib/lp/bugs/templates/bugtask-tasks-and-nominations-table-row.pt 2011-11-21 00:29:30 +0000 |
480 | @@ -164,7 +164,7 @@ |
481 | <div class="milestone-content" |
482 | tal:condition="data/target_has_milestones" |
483 | style="width: 100%; float: left"> |
484 | - <a tal:condition="view/user_can_edit_milestone" |
485 | + <a tal:condition="view/user_has_privileges" |
486 | class="nulltext addicon js-action sprite add" |
487 | tal:attributes="href data/edit_link; |
488 | style view/style_for_add_milestone"> |
489 | @@ -173,7 +173,7 @@ |
490 | <a class="value" |
491 | tal:attributes="href context/milestone/fmt:url | nothing" |
492 | tal:content="context/milestone/title | nothing" /> |
493 | - <a tal:condition="view/user_can_edit_milestone" |
494 | + <a tal:condition="view/user_has_privileges" |
495 | tal:attributes="href data/edit_link" |
496 | ><img class="editicon" src="/@@/edit" |
497 | tal:attributes="style view/style_for_edit_milestone" |
Looks good. Thanks for adding the tests. I think the one which asserts that any old user doesn't have privileges is particularly important.