Merge lp:~wallyworld/launchpad/filebug-non-bugsupervisors-1020790 into lp:launchpad
- filebug-non-bugsupervisors-1020790
- Merge into devel
Status: | Merged |
---|---|
Approved by: | j.c.sackett |
Approved revision: | no longer in the source branch. |
Merged at revision: | 15569 |
Proposed branch: | lp:~wallyworld/launchpad/filebug-non-bugsupervisors-1020790 |
Merge into: | lp:launchpad |
Diff against target: |
684 lines (+294/-44) 9 files modified
lib/lp/app/javascript/choice.js (+5/-2) lib/lp/bugs/browser/bugtarget.py (+42/-15) lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt (+5/-2) lib/lp/bugs/browser/tests/test_bugtarget_filebug.py (+76/-0) lib/lp/bugs/javascript/filebug.js (+27/-0) lib/lp/bugs/javascript/tests/test_filebug.html (+23/-1) lib/lp/bugs/javascript/tests/test_filebug.js (+80/-22) lib/lp/bugs/model/bugtask.py (+9/-2) lib/lp/bugs/templates/bugtarget-filebug-guidelines.pt (+27/-0) |
To merge this branch: | bzr merge lp:~wallyworld/launchpad/filebug-non-bugsupervisors-1020790 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
j.c.sackett (community) | Approve | ||
Review via email: mp+113471@code.launchpad.net |
Commit message
For non bug supervisors, do not show the info type widget, show only a security related checkbox.
Description of the change
== Implementation ==
This branch ensures that only people in a bug supervisor role are able to choose a specific information type with which to file a new bug. Other non privileged uses just get a "security related" checkbox.
The existing method BugTask.
== Tests ==
Add yui tests for the filebug form when rendered with the security_related checkbox.
Add new test case TestFileBugForN
== Lint ==
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
Preview Diff
1 | === modified file 'lib/lp/app/javascript/choice.js' |
2 | --- lib/lp/app/javascript/choice.js 2012-07-03 02:28:08 +0000 |
3 | +++ lib/lp/app/javascript/choice.js 2012-07-06 04:47:27 +0000 |
4 | @@ -161,8 +161,11 @@ |
5 | */ |
6 | namespace.addPopupChoiceForRadioButtons = function(field_name, choices, cfg) { |
7 | cfg = Y.merge(default_popup_choice_config, cfg); |
8 | - var legacy_node = cfg.container.one('[name="field.' + field_name + '"]') |
9 | - .ancestor('table.radio-button-widget'); |
10 | + var field_node = cfg.container.one('[name="field.' + field_name + '"]'); |
11 | + if (!Y.Lang.isValue(field_node)) { |
12 | + return; |
13 | + } |
14 | + var legacy_node = field_node.ancestor('table.radio-button-widget'); |
15 | if (!Y.Lang.isValue(legacy_node)) { |
16 | return; |
17 | } |
18 | |
19 | === modified file 'lib/lp/bugs/browser/bugtarget.py' |
20 | --- lib/lp/bugs/browser/bugtarget.py 2012-06-29 08:40:05 +0000 |
21 | +++ lib/lp/bugs/browser/bugtarget.py 2012-07-06 04:47:27 +0000 |
22 | @@ -121,7 +121,6 @@ |
23 | from lp.registry.enums import ( |
24 | InformationType, |
25 | PRIVATE_INFORMATION_TYPES, |
26 | - PUBLIC_INFORMATION_TYPES, |
27 | SECURITY_INFORMATION_TYPES, |
28 | ) |
29 | from lp.registry.interfaces.distribution import IDistribution |
30 | @@ -260,7 +259,10 @@ |
31 | @property |
32 | def field_names(self): |
33 | """Return the list of field names to display.""" |
34 | - return ['information_type'] |
35 | + if self.is_bug_supervisor: |
36 | + return ['information_type'] |
37 | + else: |
38 | + return ['security_related'] |
39 | |
40 | custom_widget('information_type', LaunchpadRadioWidgetWithDescription) |
41 | |
42 | @@ -298,11 +300,17 @@ |
43 | """Set up the form fields. See `LaunchpadFormView`.""" |
44 | super(FileBugReportingGuidelines, self).setUpFields() |
45 | |
46 | - information_type_field = copy_field( |
47 | - IBug['information_type'], readonly=False, |
48 | - vocabulary=InformationTypeVocabulary(self.context)) |
49 | - self.form_fields = self.form_fields.omit('information_type') |
50 | - self.form_fields += Fields(information_type_field) |
51 | + if self.is_bug_supervisor: |
52 | + information_type_field = copy_field( |
53 | + IBug['information_type'], readonly=False, |
54 | + vocabulary=InformationTypeVocabulary(self.context)) |
55 | + self.form_fields = self.form_fields.omit('information_type') |
56 | + self.form_fields += Fields(information_type_field) |
57 | + else: |
58 | + security_related_field = copy_field( |
59 | + IBug['security_related'], readonly=False) |
60 | + self.form_fields = self.form_fields.omit('security_related') |
61 | + self.form_fields += Fields(security_related_field) |
62 | |
63 | @property |
64 | def initial_values(self): |
65 | @@ -360,6 +368,13 @@ |
66 | else: |
67 | return self.context |
68 | |
69 | + @cachedproperty |
70 | + def is_bug_supervisor(self): |
71 | + """ Return True if the logged in user is a bug supervisor.""" |
72 | + context = self.getMainContext() |
73 | + return BugTask.userHasBugSupervisorPrivilegesContext( |
74 | + context, self.user) |
75 | + |
76 | |
77 | class FileBugViewBase(FileBugReportingGuidelines, LaunchpadFormView): |
78 | """Base class for views related to filing a bug.""" |
79 | @@ -435,9 +450,14 @@ |
80 | def field_names(self): |
81 | """Return the list of field names to display.""" |
82 | context = self.context |
83 | - field_names = ['title', 'comment', 'tags', 'information_type', |
84 | + field_names = ['title', 'comment', 'tags'] |
85 | + if self.is_bug_supervisor: |
86 | + field_names.append('information_type') |
87 | + else: |
88 | + field_names.append('security_related') |
89 | + field_names.extend([ |
90 | 'bug_already_reported_as', 'filecontent', 'patch', |
91 | - 'attachment_description', 'subscribe_to_existing_bug'] |
92 | + 'attachment_description', 'subscribe_to_existing_bug']) |
93 | if (IDistribution.providedBy(context) or |
94 | IDistributionSourcePackage.providedBy(context)): |
95 | field_names.append('packagename') |
96 | @@ -453,9 +473,7 @@ |
97 | # selected project supports them. |
98 | include_extra_fields = IProjectGroup.providedBy(context) |
99 | if not include_extra_fields: |
100 | - include_extra_fields = ( |
101 | - BugTask.userHasBugSupervisorPrivilegesContext( |
102 | - context, self.user)) |
103 | + include_extra_fields = self.is_bug_supervisor |
104 | |
105 | if include_extra_fields: |
106 | field_names.extend( |
107 | @@ -612,6 +630,7 @@ |
108 | packagename = data.get("packagename") |
109 | information_type = data.get( |
110 | "information_type", InformationType.PUBLIC) |
111 | + security_related = data.get("security_related", False) |
112 | distribution = data.get( |
113 | "distribution", getUtility(ILaunchBag).distribution) |
114 | |
115 | @@ -630,6 +649,14 @@ |
116 | if self.request.form.get("packagename_option") == "none": |
117 | packagename = None |
118 | |
119 | + if not self.is_bug_supervisor: |
120 | + # If the old UI is enabled, security bugs are always embargoed |
121 | + # when filed, but can be disclosed after they've been reported. |
122 | + if security_related: |
123 | + information_type = InformationType.EMBARGOEDSECURITY |
124 | + else: |
125 | + information_type = InformationType.PUBLIC |
126 | + |
127 | linkified_ack = structured(FormattersAPI( |
128 | self.getAcknowledgementMessage(self.context)).text_to_html( |
129 | last_paragraph_class="last")) |
130 | @@ -665,12 +692,12 @@ |
131 | notifications.append( |
132 | 'Additional information was added to the bug description.') |
133 | |
134 | - if extra_data.private: |
135 | - if params.information_type in PUBLIC_INFORMATION_TYPES: |
136 | + if not self.is_bug_supervisor and extra_data.private: |
137 | + if params.information_type == InformationType.PUBLIC: |
138 | params.information_type = InformationType.USERDATA |
139 | |
140 | # Apply any extra options given by privileged users. |
141 | - if BugTask.userHasBugSupervisorPrivilegesContext(context, self.user): |
142 | + if self.is_bug_supervisor: |
143 | if 'assignee' in data: |
144 | params.assignee = data['assignee'] |
145 | if 'status' in data: |
146 | |
147 | === modified file 'lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt' |
148 | --- lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2012-06-22 05:52:17 +0000 |
149 | +++ lib/lp/bugs/browser/tests/bugtarget-filebug-views.txt 2012-07-06 04:47:27 +0000 |
150 | @@ -688,7 +688,7 @@ |
151 | >>> from lp.registry.enums import InformationType |
152 | >>> bug_data = dict( |
153 | ... title=u'Security bug', comment=u'Test description.', |
154 | - ... information_type=InformationType.EMBARGOEDSECURITY) |
155 | + ... security_related=u'on') |
156 | |
157 | >>> filebug_view = create_initialized_view(ubuntu_firefox, '+filebug') |
158 | >>> filebug_view.validate(bug_data) is None |
159 | @@ -720,8 +720,10 @@ |
160 | >>> product.setBugSupervisor(owner, owner) |
161 | >>> supervisor_fields = set(filebug_view.field_names) |
162 | |
163 | -Privileged users get all the same fields as normal users, plus a few extra. |
164 | +Privileged users get most of the same fields as normal users, plus a few extra. |
165 | +The security_related checkbox is replaced by an information_type radio group. |
166 | |
167 | + >>> normal_fields.remove('security_related') |
168 | >>> owner_fields == supervisor_fields |
169 | True |
170 | >>> supervisor_fields.issuperset(normal_fields) |
171 | @@ -731,6 +733,7 @@ |
172 | ... print field |
173 | assignee |
174 | importance |
175 | + information_type |
176 | milestone |
177 | status |
178 | |
179 | |
180 | === modified file 'lib/lp/bugs/browser/tests/test_bugtarget_filebug.py' |
181 | --- lib/lp/bugs/browser/tests/test_bugtarget_filebug.py 2012-06-20 05:25:44 +0000 |
182 | +++ lib/lp/bugs/browser/tests/test_bugtarget_filebug.py 2012-07-06 04:47:27 +0000 |
183 | @@ -421,6 +421,19 @@ |
184 | for info_type in InformationType: |
185 | self.assertIsNotNone(soup.find('label', text=info_type.title)) |
186 | |
187 | + def test_filebug_view_renders_info_type_widget(self): |
188 | + # The info type widget is rendered for bug supervisor roles. |
189 | + product = self.factory.makeProduct(official_malone=True) |
190 | + with person_logged_in(product.owner): |
191 | + view = create_initialized_view( |
192 | + product, '+filebug', principal=product.owner) |
193 | + html = view.render() |
194 | + soup = BeautifulSoup(html) |
195 | + self.assertIsNone( |
196 | + soup.find('input', attrs={'name': 'field.security_related'})) |
197 | + self.assertIsNotNone( |
198 | + soup.find('input', attrs={'name': 'field.information_type'})) |
199 | + |
200 | def test_filebug_information_type_vocabulary_private_projects(self): |
201 | # The vocabulary for information_type when filing a bug only has |
202 | # private info types for private bug projects. |
203 | @@ -437,6 +450,69 @@ |
204 | self.assertIsNone(soup.find('label', text=info_type.title)) |
205 | |
206 | |
207 | +class TestFileBugForNonBugSupervisors(TestCaseWithFactory): |
208 | + |
209 | + layer = DatabaseFunctionalLayer |
210 | + |
211 | + def filebug_via_view(self, private_bugs=False, security_related=False): |
212 | + form = { |
213 | + 'field.title': 'A bug', |
214 | + 'field.comment': 'A comment', |
215 | + 'field.security_related': 'on' if security_related else '', |
216 | + 'field.actions.submit_bug': 'Submit Bug Request', |
217 | + } |
218 | + product = self.factory.makeProduct(official_malone=True) |
219 | + if private_bugs: |
220 | + removeSecurityProxy(product).private_bugs = True |
221 | + anyone = self.factory.makePerson() |
222 | + with person_logged_in(anyone): |
223 | + view = create_initialized_view( |
224 | + product, '+filebug', form=form, principal=anyone) |
225 | + bug_url = view.request.response.getHeader('Location') |
226 | + bug_number = bug_url.split('/')[-1] |
227 | + return getUtility(IBugSet).getByNameOrID(bug_number) |
228 | + |
229 | + def test_filebug_non_security_related(self): |
230 | + # Non security related bugs are PUBLIC for products with |
231 | + # private_bugs=False. |
232 | + bug = self.filebug_via_view() |
233 | + self.assertEqual(InformationType.PUBLIC, bug.information_type) |
234 | + |
235 | + def test_filebug_security_related(self): |
236 | + # Security related bugs are EMBARGOEDSECURITY for products with |
237 | + # private_bugs=False. |
238 | + bug = self.filebug_via_view(security_related=True) |
239 | + self.assertEqual( |
240 | + InformationType.EMBARGOEDSECURITY, bug.information_type) |
241 | + |
242 | + def test_filebug_security_related_with_private_bugs(self): |
243 | + # Security related bugs are EMBARGOEDSECURITY for products with |
244 | + # private_bugs=True. |
245 | + bug = self.filebug_via_view(private_bugs=True, security_related=True) |
246 | + self.assertEqual( |
247 | + InformationType.EMBARGOEDSECURITY, bug.information_type) |
248 | + |
249 | + def test_filebug_with_private_bugs(self): |
250 | + # Non security related bugs are USERDATA for products with |
251 | + # private_bugs=True. |
252 | + bug = self.filebug_via_view(private_bugs=True) |
253 | + self.assertEqual(InformationType.USERDATA, bug.information_type) |
254 | + |
255 | + def test_filebug_view_renders_security_related(self): |
256 | + # The security_related checkbox is rendered for non bug supervisors. |
257 | + product = self.factory.makeProduct(official_malone=True) |
258 | + anyone = self.factory.makePerson() |
259 | + with person_logged_in(anyone): |
260 | + view = create_initialized_view( |
261 | + product, '+filebug', principal=anyone) |
262 | + html = view.render() |
263 | + soup = BeautifulSoup(html) |
264 | + self.assertIsNotNone( |
265 | + soup.find('input', attrs={'name': 'field.security_related'})) |
266 | + self.assertIsNone( |
267 | + soup.find('input', attrs={'name': 'field.information_type'})) |
268 | + |
269 | + |
270 | class TestFileBugSourcePackage(TestCaseWithFactory): |
271 | |
272 | layer = DatabaseFunctionalLayer |
273 | |
274 | === modified file 'lib/lp/bugs/javascript/filebug.js' |
275 | --- lib/lp/bugs/javascript/filebug.js 2012-06-27 14:05:07 +0000 |
276 | +++ lib/lp/bugs/javascript/filebug.js 2012-07-06 04:47:27 +0000 |
277 | @@ -29,8 +29,13 @@ |
278 | search_button.set('value', 'Check again'); |
279 | } |
280 | setup_information_type(); |
281 | + setup_security_related(); |
282 | setupChoiceWidgets(); |
283 | + set_default_privacy_banner(); |
284 | } |
285 | +}; |
286 | + |
287 | +var set_default_privacy_banner = function() { |
288 | var filebug_privacy_text = "This report will be private. " + |
289 | "You can disclose it later."; |
290 | update_privacy_banner( |
291 | @@ -65,6 +70,9 @@ |
292 | |
293 | var setup_information_type = function() { |
294 | var itypes_table = Y.one('.radio-button-widget'); |
295 | + if (!Y.Lang.isValue(itypes_table)) { |
296 | + return; |
297 | + } |
298 | itypes_table.delegate('change', function() { |
299 | var banner_text = get_new_banner_text(this.get('value')); |
300 | var private_type = (Y.Array.indexOf( |
301 | @@ -82,6 +90,25 @@ |
302 | 'information_type', LP.cache.information_type_data, true); |
303 | }; |
304 | |
305 | +var setup_security_related = function() { |
306 | + var security_related = Y.one('[id="field.security_related"]'); |
307 | + if (!Y.Lang.isValue(security_related)) { |
308 | + return; |
309 | + } |
310 | + var notification_text = "This report will be private " + |
311 | + "because it is a security " + |
312 | + "vulnerability. You can " + |
313 | + "disclose it later."; |
314 | + security_related.on('change', function() { |
315 | + var checked = security_related.get('checked'); |
316 | + if (checked) { |
317 | + update_privacy_banner(true, notification_text); |
318 | + } else { |
319 | + set_default_privacy_banner(); |
320 | + } |
321 | + }); |
322 | +}; |
323 | + |
324 | namespace.setup_filebug = setup_filebug; |
325 | |
326 | }, "0.1", {"requires": [ |
327 | |
328 | === modified file 'lib/lp/bugs/javascript/tests/test_filebug.html' |
329 | --- lib/lp/bugs/javascript/tests/test_filebug.html 2012-06-15 01:13:39 +0000 |
330 | +++ lib/lp/bugs/javascript/tests/test_filebug.html 2012-07-06 04:47:27 +0000 |
331 | @@ -67,7 +67,7 @@ |
332 | </ul> |
333 | <div class='login-logout'></div> |
334 | <div id="fixture"></div> |
335 | - <script type="text/x-template" id="privacy-banner-template"> |
336 | + <script type="text/x-template" id="bugsupervisor-filebug-template"> |
337 | <div id="filebug-form"> |
338 | <table class="radio-button-widget"> |
339 | <tbody> |
340 | @@ -117,5 +117,27 @@ |
341 | </div> |
342 | </div> |
343 | </script> |
344 | + <script type="text/x-template" id="filebug-template"> |
345 | + <div id="filebug-form"> |
346 | + <div> |
347 | + <input type="checkbox" value="on" name="field.security_related" id="field.security_related" class="checkboxType"> |
348 | + <label for="field.security_related"> |
349 | + This bug is a security vulnerability |
350 | + </label> |
351 | + </div> |
352 | + <div class="value"> |
353 | + <select size="1" name="field.status" id="field.status"> |
354 | + <option value="New" selected="selected">New</option> |
355 | + <option value="Incomplete">Incomplete</option> |
356 | + </select> |
357 | + </div> |
358 | + <div class="value"> |
359 | + <select size="1" name="field.importance" id="field.importance"> |
360 | + <option value="Undecided" selected="selected">Undecided</option> |
361 | + <option value="High">High</option> |
362 | + </select> |
363 | + </div> |
364 | + </div> |
365 | + </script> |
366 | </body> |
367 | </html> |
368 | |
369 | === modified file 'lib/lp/bugs/javascript/tests/test_filebug.js' |
370 | --- lib/lp/bugs/javascript/tests/test_filebug.js 2012-07-03 01:35:50 +0000 |
371 | +++ lib/lp/bugs/javascript/tests/test_filebug.js 2012-07-06 04:47:27 +0000 |
372 | @@ -33,17 +33,24 @@ |
373 | ] |
374 | } |
375 | }; |
376 | + }, |
377 | + |
378 | + setupForm: function(bugsupervisor_version) { |
379 | this.fixture = Y.one('#fixture'); |
380 | - var banner = Y.Node.create( |
381 | - Y.one('#privacy-banner-template').getContent()); |
382 | - this.fixture.appendChild(banner); |
383 | + var form_id = 'filebug-template'; |
384 | + if (bugsupervisor_version) { |
385 | + form_id = 'bugsupervisor-' + form_id; |
386 | + } |
387 | + var form = Y.Node.create(Y.one('#' + form_id).getContent()); |
388 | + this.fixture.appendChild(form); |
389 | + Y.lp.bugs.filebug.setup_filebug(true); |
390 | }, |
391 | |
392 | tearDown: function () { |
393 | - if (this.fixture !== null) { |
394 | + if (Y.Lang.isValue(this.fixture)) { |
395 | this.fixture.empty(true); |
396 | + delete this.fixture; |
397 | } |
398 | - delete this.fixture; |
399 | delete window.LP; |
400 | }, |
401 | |
402 | @@ -55,7 +62,7 @@ |
403 | |
404 | // Filing a public bug does not show the privacy banner. |
405 | test_setup_filebug_public: function () { |
406 | - Y.lp.bugs.filebug.setup_filebug(true); |
407 | + this.setupForm(true); |
408 | var banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
409 | Y.Assert.isNotNull(banner_hidden); |
410 | }, |
411 | @@ -64,7 +71,7 @@ |
412 | // banner. |
413 | test_setup_filebug_private: function () { |
414 | window.LP.cache.bug_private_by_default = true; |
415 | - Y.lp.bugs.filebug.setup_filebug(true); |
416 | + this.setupForm(true); |
417 | var banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
418 | Y.Assert.isNull(banner_hidden); |
419 | var banner_text = Y.one('.banner-text').get('text'); |
420 | @@ -76,7 +83,7 @@ |
421 | // Selecting a private info type using the legacy radio buttons |
422 | // turns on the privacy banner. |
423 | test_legacy_select_private_info_type: function () { |
424 | - Y.lp.bugs.filebug.setup_filebug(true); |
425 | + this.setupForm(true); |
426 | var banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
427 | Y.Assert.isNotNull(banner_hidden); |
428 | Y.one('[id="field.information_type.2"]').simulate('click'); |
429 | @@ -92,7 +99,7 @@ |
430 | // turns off the privacy banner. |
431 | test_legacy_select_public_info_type: function () { |
432 | window.LP.cache.bug_private_by_default = true; |
433 | - Y.lp.bugs.filebug.setup_filebug(true); |
434 | + this.setupForm(true); |
435 | Y.one('[id="field.information_type.2"]').simulate('click'); |
436 | var banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
437 | Y.Assert.isNull(banner_hidden); |
438 | @@ -101,6 +108,52 @@ |
439 | Y.Assert.isNotNull(banner_hidden); |
440 | }, |
441 | |
442 | + // When non bug supervisors select a security related bug the privacy |
443 | + // banner is turned on. |
444 | + test_select_security_related: function () { |
445 | + this.setupForm(false); |
446 | + var banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
447 | + Y.Assert.isNotNull(banner_hidden); |
448 | + Y.one('[id="field.security_related"]').simulate('click'); |
449 | + banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
450 | + Y.Assert.isNull(banner_hidden); |
451 | + var banner_text = Y.one('.banner-text').get('text'); |
452 | + Y.Assert.areEqual( |
453 | + 'This report will be private because it is a ' + |
454 | + 'security vulnerability. You can disclose it later.', |
455 | + banner_text); |
456 | + }, |
457 | + |
458 | + // When non bug supervisors unselect a security related bug the privacy |
459 | + // banner is turned off. |
460 | + test_unselect_security_related: function () { |
461 | + this.setupForm(false); |
462 | + Y.one('[id="field.security_related"]').simulate('click'); |
463 | + var banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
464 | + Y.Assert.isNull(banner_hidden); |
465 | + Y.one('[id="field.security_related"]').simulate('click'); |
466 | + banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
467 | + Y.Assert.isNotNull(banner_hidden); |
468 | + }, |
469 | + |
470 | + // When non bug supervisors unselect a security related bug the privacy |
471 | + // banner remains on for private_by_default bugs. |
472 | + test_unselect_security_related_default_private: function () { |
473 | + window.LP.cache.bug_private_by_default = true; |
474 | + this.setupForm(false); |
475 | + Y.one('[id="field.security_related"]').simulate('click'); |
476 | + var banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
477 | + Y.Assert.isNull(banner_hidden); |
478 | + Y.one('[id="field.security_related"]').simulate('click'); |
479 | + banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
480 | + Y.Assert.isNull(banner_hidden); |
481 | + var banner_text = Y.one('.banner-text').get('text'); |
482 | + Y.Assert.areEqual( |
483 | + 'This report will be private. ' + |
484 | + 'You can disclose it later.', |
485 | + banner_text); |
486 | + }, |
487 | + |
488 | // The dupe finder functionality is setup. |
489 | test_dupe_finder_setup: function () { |
490 | window.LP.cache.enable_bugfiling_duplicate_search = true; |
491 | @@ -116,7 +169,7 @@ |
492 | Y.lp.bugs.filebug_dupefinder.setup_dupes = function() { |
493 | setup_dupes_called = true; |
494 | }; |
495 | - Y.lp.bugs.filebug.setup_filebug(true); |
496 | + this.setupForm(true); |
497 | Y.Assert.isTrue(setup_dupe_finder_called); |
498 | Y.Assert.isTrue(setup_dupes_called); |
499 | Y.lp.bugs.filebug_dupefinder.setup_dupes = orig_setup_dupes; |
500 | @@ -126,7 +179,7 @@ |
501 | |
502 | // The bugtask status choice popup is rendered. |
503 | test_status_setup: function () { |
504 | - Y.lp.bugs.filebug.setup_filebug(true); |
505 | + this.setupForm(true); |
506 | var status_node = Y.one('.status-content .value'); |
507 | Y.Assert.areEqual('New', status_node.get('text')); |
508 | var status_edit_node = Y.one('.status-content a.sprite.edit'); |
509 | @@ -135,9 +188,7 @@ |
510 | Y.Assert.isTrue(legacy_dropdown.hasClass('unseen')); |
511 | }, |
512 | |
513 | - // The bugtask importance choice popup is rendered. |
514 | - test_importance_setup: function () { |
515 | - Y.lp.bugs.filebug.setup_filebug(true); |
516 | + _perform_test_importance: function() { |
517 | var importance_node = Y.one('.importance-content .value'); |
518 | Y.Assert.areEqual('Undecided', importance_node.get('text')); |
519 | var importance_edit_node = |
520 | @@ -147,17 +198,24 @@ |
521 | Y.Assert.isTrue(legacy_dropdown.hasClass('unseen')); |
522 | }, |
523 | |
524 | + // The bugtask importance choice popup is rendered. |
525 | + test_importance_setup: function () { |
526 | + this.setupForm(true); |
527 | + this._perform_test_importance(); |
528 | + }, |
529 | + |
530 | // The choice popup wiring works even if the field is missing. |
531 | // This is so fields which the user does not have permission to see |
532 | // can be missing and everything still works as expected. |
533 | test_missing_fields: function () { |
534 | + this.setupForm(true); |
535 | Y.one('[id="field.status"]').remove(true); |
536 | - this.test_importance_setup(); |
537 | + this._perform_test_importance(); |
538 | }, |
539 | |
540 | // The bugtask status choice popup updates the form. |
541 | test_status_selection: function() { |
542 | - Y.lp.bugs.filebug.setup_filebug(true); |
543 | + this.setupForm(true); |
544 | var status_popup = Y.one('.status-content a'); |
545 | status_popup.simulate('click'); |
546 | var status_choice = Y.one( |
547 | @@ -169,7 +227,7 @@ |
548 | |
549 | // The bugtask importance choice popup updates the form. |
550 | test_importance_selection: function() { |
551 | - Y.lp.bugs.filebug.setup_filebug(true); |
552 | + this.setupForm(true); |
553 | var status_popup = Y.one('.importance-content a'); |
554 | status_popup.simulate('click'); |
555 | var status_choice = Y.one( |
556 | @@ -181,7 +239,7 @@ |
557 | |
558 | // The bugtask information_type choice popup is rendered. |
559 | test_information_type_setup: function () { |
560 | - Y.lp.bugs.filebug.setup_filebug(true); |
561 | + this.setupForm(true); |
562 | var information_type_node = |
563 | Y.one('.information_type-content .value'); |
564 | Y.Assert.areEqual('Public', information_type_node.get('text')); |
565 | @@ -194,7 +252,7 @@ |
566 | |
567 | // The bugtask information_type choice popup updates the form. |
568 | test_information_type_selection: function() { |
569 | - Y.lp.bugs.filebug.setup_filebug(true); |
570 | + this.setupForm(true); |
571 | var information_type_popup = Y.one('.information_type-content a'); |
572 | information_type_popup.simulate('click'); |
573 | var header_text = |
574 | @@ -211,7 +269,7 @@ |
575 | // Selecting a private info type using the popup choice widget |
576 | // turns on the privacy banner. |
577 | test_select_private_info_type: function () { |
578 | - Y.lp.bugs.filebug.setup_filebug(true); |
579 | + this.setupForm(true); |
580 | var banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
581 | Y.Assert.isNotNull(banner_hidden); |
582 | var information_type_popup = Y.one('.information_type-content a'); |
583 | @@ -229,7 +287,7 @@ |
584 | |
585 | test_select_private_info_type_with_private_flag: function () { |
586 | window.LP.cache.show_userdata_as_private = true; |
587 | - Y.lp.bugs.filebug.setup_filebug(true); |
588 | + this.setupForm(true); |
589 | var banner_hidden = Y.one('.yui3-privacybanner-hidden'); |
590 | Y.Assert.isNotNull(banner_hidden); |
591 | var information_type_popup = Y.one('.information_type-content a'); |
592 | @@ -248,7 +306,7 @@ |
593 | // turns off the privacy banner. |
594 | test_select_public_info_type: function () { |
595 | window.LP.cache.bug_private_by_default = true; |
596 | - Y.lp.bugs.filebug.setup_filebug(true); |
597 | + this.setupForm(true); |
598 | var information_type_popup = Y.one('.information_type-content a'); |
599 | information_type_popup.simulate('click'); |
600 | var information_type_choice = Y.one( |
601 | |
602 | === modified file 'lib/lp/bugs/model/bugtask.py' |
603 | --- lib/lp/bugs/model/bugtask.py 2012-07-03 08:04:35 +0000 |
604 | +++ lib/lp/bugs/model/bugtask.py 2012-07-06 04:47:27 +0000 |
605 | @@ -96,6 +96,7 @@ |
606 | UserCannotEditBugTaskMilestone, |
607 | UserCannotEditBugTaskStatus, |
608 | ) |
609 | +from lp.bugs.interfaces.bugtarget import IBugTarget |
610 | from lp.registry.enums import ( |
611 | InformationType, |
612 | PUBLIC_INFORMATION_TYPES, |
613 | @@ -1329,8 +1330,11 @@ |
614 | return True |
615 | |
616 | # If you're the owner or a driver, you can change bug details. |
617 | + owner_context = context |
618 | + if IBugTarget.providedBy(context): |
619 | + owner_context = context.pillar |
620 | return ( |
621 | - role.isOwner(context.pillar) or role.isOneOfDrivers(context)) |
622 | + role.isOwner(owner_context) or role.isOneOfDrivers(context)) |
623 | |
624 | @classmethod |
625 | def userHasBugSupervisorPrivilegesContext(cls, context, user): |
626 | @@ -1344,9 +1348,12 @@ |
627 | role = IPersonRoles(user) |
628 | # If you have driver privileges, or are the bug supervisor, you can |
629 | # change bug details. |
630 | + supervisor_context = context |
631 | + if IBugTarget.providedBy(context): |
632 | + supervisor_context = context.pillar |
633 | return ( |
634 | cls.userHasDriverPrivilegesContext(context, user) or |
635 | - role.isBugSupervisor(context.pillar)) |
636 | + role.isBugSupervisor(supervisor_context)) |
637 | |
638 | def userHasDriverPrivileges(self, user): |
639 | """See `IBugTask`.""" |
640 | |
641 | === modified file 'lib/lp/bugs/templates/bugtarget-filebug-guidelines.pt' |
642 | --- lib/lp/bugs/templates/bugtarget-filebug-guidelines.pt 2012-06-20 05:25:44 +0000 |
643 | +++ lib/lp/bugs/templates/bugtarget-filebug-guidelines.pt 2012-07-06 04:47:27 +0000 |
644 | @@ -11,6 +11,7 @@ |
645 | </tr> |
646 | |
647 | <tr tal:define="security_context view/getMainContext"> |
648 | + <tal:information_type tal:condition="view/is_bug_supervisor"> |
649 | <td colspan="2" width="100%" |
650 | tal:define="widget nocall: view/widgets/information_type|nothing" |
651 | tal:condition="widget"> |
652 | @@ -19,6 +20,32 @@ |
653 | </label> |
654 | <input tal:replace="structure widget" /> |
655 | </td> |
656 | + </tal:information_type> |
657 | + <tal:security_related tal:condition="not: view/is_bug_supervisor"> |
658 | + <td colspan="2" width="100%" |
659 | + tal:define="widget nocall: view/widgets/security_related|nothing" |
660 | + tal:condition="widget"> |
661 | + <table> |
662 | + <tbody> |
663 | + <tr> |
664 | + <td> |
665 | + <input type="checkbox" tal:replace="structure widget" /> |
666 | + </td> |
667 | + <td> |
668 | + <label tal:attributes="for widget/name"> |
669 | + This bug is a security vulnerability |
670 | + </label> |
671 | + <div> |
672 | + The security group for |
673 | + <tal:security-context content="security_context/displayname" /> |
674 | + will be notified. |
675 | + </div> |
676 | + </td> |
677 | + </tr> |
678 | + </tbody> |
679 | + </table> |
680 | + </td> |
681 | + </tal:security_related> |
682 | </tr> |
683 | </tbody></table></td></tr> |
684 | </tal:root> |
Looks good, thanks.