Merge lp:~wallyworld/launchpad/filebug-non-bugsupervisors-1020790 into lp:launchpad

Proposed by Ian Booth
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
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.userHasBugSupervisorPrivilegesContext() was used to determine if a user is considered a bug supervisor. So this includes admins, drivers etc in the allowed list.

== Tests ==

Add yui tests for the filebug form when rendered with the security_related checkbox.
Add new test case TestFileBugForNonBugSupervisors to check that the form rendering and submission works as expected

== Lint ==

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/app/javascript/choice.js
  lib/lp/bugs/browser/bugtarget.py
  lib/lp/bugs/browser/tests/test_bugtarget_filebug.py
  lib/lp/bugs/javascript/filebug.js
  lib/lp/bugs/javascript/tests/test_filebug.html
  lib/lp/bugs/javascript/tests/test_filebug.js
  lib/lp/bugs/templates/bugtarget-filebug-guidelines.pt

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

Looks good, thanks.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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>