Merge lp:~stevenk/launchpad/bugs-information_type-ui-secrecy into lp:launchpad

Proposed by Steve Kowalik on 2012-04-19
Status: Merged
Approved by: Steve Kowalik on 2012-04-19
Approved revision: no longer in the source branch.
Merged at revision: 15123
Proposed branch: lp:~stevenk/launchpad/bugs-information_type-ui-secrecy
Merge into: lp:launchpad
Diff against target: 219 lines (+103/-26)
5 files modified
lib/lp/bugs/browser/bug.py (+59/-25)
lib/lp/bugs/browser/tests/test_bug_views.py (+33/-0)
lib/lp/bugs/interfaces/bug.py (+1/-1)
lib/lp/registry/vocabularies.py (+3/-0)
lib/lp/services/features/flags.py (+7/-0)
To merge this branch: bzr merge lp:~stevenk/launchpad/bugs-information_type-ui-secrecy
Reviewer Review Type Date Requested Status
Ian Booth (community) 2012-04-19 Approve on 2012-04-19
Review via email: mp+102624@code.launchpad.net

Commit Message

Show information_type in Bug:+secrecy if a feature flag is set.

Description of the Change

Add a new feature flag, disclosure.show_information_type_in_ui.enabled, and make use of it inside Bug:+secrecy. I've been quite careful with the changes I've made and I have left the existing tests alone, and added new ones that enable the feature flag and make sure we can change the information_type and that the vocab is used correctly.

I've also cleaned up a little bit of lint, and made sure that InformationTypeVocabulary works with the widget we're using.

To post a comment you must log in.
Ian Booth (wallyworld) wrote :

Nice branch. Yay for adding more cool new stuff.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/browser/bug.py'
2--- lib/lp/bugs/browser/bug.py 2012-04-11 07:10:12 +0000
3+++ lib/lp/bugs/browser/bug.py 2012-04-19 05:58:50 +0000
4@@ -623,6 +623,7 @@
5 return 'Private'
6 return title
7
8+
9 class BugActivity(BugView):
10
11 page_title = 'Activity log'
12@@ -861,23 +862,53 @@
13
14 @property
15 def label(self):
16- return 'Bug #%i - Set visibility and security' % self.context.bug.id
17+ label = 'Bug #%i - Set ' % self.context.bug.id
18+ if bool(getFeatureFlag(
19+ 'disclosure.show_information_type_in_ui.enabled')):
20+ label += 'Information type'
21+ else:
22+ label += 'visibility and security'
23+ return label
24
25 page_title = label
26
27- class schema(Interface):
28- """Schema for editing secrecy info."""
29- private_field = copy_field(IBug['private'], readonly=False)
30- security_related_field = copy_field(
31- IBug['security_related'], readonly=False)
32+ @property
33+ def field_names(self):
34+ if bool(getFeatureFlag(
35+ 'disclosure.show_information_type_in_ui.enabled')):
36+ return ['information_type']
37+ else:
38+ return ['private', 'security_related']
39+
40+ custom_widget('information_type', LaunchpadRadioWidgetWithDescription)
41+
42+ @property
43+ def schema(self):
44+ """Schema for editing the information type of a `IBug`."""
45+ class privacy_schema(Interface):
46+ private_field = copy_field(IBug['private'], readonly=False)
47+ security_related_field = copy_field(
48+ IBug['security_related'], readonly=False)
49+
50+ class information_type_schema(Interface):
51+ information_type_field = copy_field(
52+ IBug['information_type'], readonly=False,
53+ vocabulary=InformationTypeVocabulary())
54+ if bool(getFeatureFlag(
55+ 'disclosure.show_information_type_in_ui.enabled')):
56+ return information_type_schema
57+ else:
58+ return privacy_schema
59
60 def setUpFields(self):
61 """See `LaunchpadFormView`."""
62 super(BugSecrecyEditView, self).setUpFields()
63- bug = self.context.bug
64- if (bug.information_type == InformationType.PROPRIETARY
65- and len(bug.affected_pillars) > 1):
66- self.form_fields = self.form_fields.omit('private')
67+ if not bool(getFeatureFlag(
68+ 'disclosure.show_information_type_in_ui.enabled')):
69+ bug = self.context.bug
70+ if (bug.information_type == InformationType.PROPRIETARY
71+ and len(bug.affected_pillars) > 1):
72+ self.form_fields = self.form_fields.omit('private')
73
74 @property
75 def next_url(self):
76@@ -891,29 +922,32 @@
77 @property
78 def initial_values(self):
79 """See `LaunchpadFormView.`"""
80- return {'private': self.context.bug.private,
81+ if bool(getFeatureFlag(
82+ 'disclosure.show_information_type_in_ui.enabled')):
83+ return {'information_type': self.context.bug.information_type}
84+ else:
85+ return {
86+ 'private': self.context.bug.private,
87 'security_related': self.context.bug.security_related}
88
89 @action('Change', name='change')
90 def change_action(self, action, data):
91 """Update the bug."""
92- # We will modify data later, so take a copy now.
93 data = dict(data)
94-
95- # We handle privacy changes by hand instead of leaving it to
96- # the usual machinery because we must use
97- # bug.transitionToInformationType() to ensure auditing information is
98- # recorded.
99 bug = self.context.bug
100- private = data.pop('private', bug.private)
101+ if bool(getFeatureFlag(
102+ 'disclosure.show_information_type_in_ui.enabled')):
103+ information_type = data.pop('information_type')
104+ else:
105+ private = data.pop('private', bug.private)
106+ security_related = data.pop('security_related')
107+ information_type = convert_to_information_type(
108+ private, security_related)
109 user_will_be_subscribed = (
110- private and bug.getSubscribersForPerson(self.user).is_empty())
111- security_related = data.pop('security_related')
112- user = getUtility(ILaunchBag).user
113- # This will change when the UI does.
114- information_type = convert_to_information_type(
115- private, security_related)
116- changed = bug.transitionToInformationType(information_type, user)
117+ information_type in PRIVATE_INFORMATION_TYPES and
118+ bug.getSubscribersForPerson(self.user).is_empty())
119+ changed = bug.transitionToInformationType(
120+ information_type, self.user)
121 if changed:
122 self._handlePrivacyChanged(user_will_be_subscribed)
123 if self.request.is_ajax:
124
125=== modified file 'lib/lp/bugs/browser/tests/test_bug_views.py'
126--- lib/lp/bugs/browser/tests/test_bug_views.py 2012-04-04 05:46:26 +0000
127+++ lib/lp/bugs/browser/tests/test_bug_views.py 2012-04-19 05:58:50 +0000
128@@ -406,6 +406,39 @@
129 with person_logged_in(owner):
130 self.assertTrue(bug.security_related)
131
132+ def test_set_information_type(self):
133+ # Test that the bug's information_type can be updated using the
134+ # view with the feature flag on.
135+ bug = self.factory.makeBug()
136+ feature_flag = {
137+ 'disclosure.show_information_type_in_ui.enabled': 'on'}
138+ with FeatureFixture(feature_flag):
139+ with person_logged_in(bug.owner):
140+ view = create_initialized_view(
141+ bug.default_bugtask, name='+secrecy', form={
142+ 'field.information_type': 'USERDATA',
143+ 'field.actions.change': 'Change'})
144+ self.assertEqual([], view.errors)
145+ self.assertEqual(InformationType.USERDATA, bug.information_type)
146+
147+ def test_information_type_vocabulary(self):
148+ # Test that the view creates the vocabulary correctly.
149+ bug = self.factory.makeBug()
150+ feature_flags = {
151+ 'disclosure.show_information_type_in_ui.enabled': 'on',
152+ 'disclosure.proprietary_information_type.disabled': 'on',
153+ 'disclosure.display_userdata_as_private.enabled': 'on'}
154+ with FeatureFixture(feature_flags):
155+ with person_logged_in(bug.owner):
156+ view = create_initialized_view(
157+ bug.default_bugtask, name='+secrecy',
158+ principal=bug.owner)
159+ html = view.render()
160+ soup = BeautifulSoup(html)
161+ self.assertEqual(u'Private', soup.find('label', text="Private"))
162+ self.assertIs(None, soup.find('label', text="User Data"))
163+ self.assertIs(None, soup.find('label', text="Proprietary"))
164+
165
166 class TestBugTextViewPrivateTeams(TestCaseWithFactory):
167 """ Test for rendering BugTextView with private team artifacts.
168
169=== modified file 'lib/lp/bugs/interfaces/bug.py'
170--- lib/lp/bugs/interfaces/bug.py 2012-04-16 23:55:51 +0000
171+++ lib/lp/bugs/interfaces/bug.py 2012-04-19 05:58:50 +0000
172@@ -214,7 +214,7 @@
173 information_type = exported(
174 Choice(
175 title=_('Information Type'), vocabulary=InformationType,
176- required=False, readonly=True,
177+ required=True, readonly=True, default=InformationType.PUBLIC,
178 description=_(
179 'The type of information contained in this bug report.')))
180
181
182=== modified file 'lib/lp/registry/vocabularies.py'
183--- lib/lp/registry/vocabularies.py 2012-04-12 07:28:33 +0000
184+++ lib/lp/registry/vocabularies.py 2012-04-19 05:58:50 +0000
185@@ -65,6 +65,7 @@
186
187 from operator import attrgetter
188
189+from lazr.enum import IEnumeratedType
190 from lazr.restful.interfaces import IReference
191 from lazr.restful.utils import safe_hasattr
192 from sqlobject import (
193@@ -2228,6 +2229,8 @@
194
195 class InformationTypeVocabulary(SimpleVocabulary):
196
197+ implements(IEnumeratedType)
198+
199 def __init__(self):
200 types = [
201 InformationType.PUBLIC,
202
203=== modified file 'lib/lp/services/features/flags.py'
204--- lib/lp/services/features/flags.py 2012-04-18 22:54:23 +0000
205+++ lib/lp/services/features/flags.py 2012-04-19 05:58:50 +0000
206@@ -339,6 +339,13 @@
207 '',
208 '',
209 ''),
210+ ('disclosure.show_information_type_in_ui.enabled',
211+ 'boolean',
212+ ('If true, displays the information_type directly in the UI when '
213+ 'filing a bug and changing the information_type.'),
214+ '',
215+ '',
216+ ''),
217 ])
218
219 # The set of all flag names that are documented.