Merge lp:~cjwatson/launchpad/fix-dsp-vocab-picker into lp:launchpad
- fix-dsp-vocab-picker
- Merge into devel
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Merged at revision: | 18164 | ||||||||
Proposed branch: | lp:~cjwatson/launchpad/fix-dsp-vocab-picker | ||||||||
Merge into: | lp:launchpad | ||||||||
Diff against target: |
781 lines (+216/-173) 12 files modified
lib/lp/app/javascript/picker/picker_patcher.js (+4/-2) lib/lp/app/javascript/picker/tests/test_picker_patcher.js (+35/-2) lib/lp/app/widgets/popup.py (+14/-1) lib/lp/app/widgets/templates/distributionsourcepackage-picker.pt (+29/-0) lib/lp/app/widgets/templates/form-picker-macros.pt (+1/-1) lib/lp/app/widgets/tests/test_launchpadtarget.py (+2/-12) lib/lp/app/widgets/tests/test_popup.py (+33/-7) lib/lp/bugs/browser/tests/test_bugalsoaffects.py (+1/-18) lib/lp/bugs/browser/widgets/bugtask.py (+11/-2) lib/lp/registry/tests/test_distributionsourcepackage_vocabulary.py (+45/-92) lib/lp/registry/vocabularies.py (+29/-35) lib/lp/services/webapp/configure.zcml (+12/-1) |
||||||||
To merge this branch: | bzr merge lp:~cjwatson/launchpad/fix-dsp-vocab-picker | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+300782@code.launchpad.net |
Commit message
Customise the picker for DistributionSou
Description of the change
Customise the picker for DistributionSou
I've also changed the vocabulary itself to return just the package name as the term's token and title, rather than distribution/
William Grant (wgrant) : | # |
Preview Diff
1 | === modified file 'lib/lp/app/javascript/picker/picker_patcher.js' | |||
2 | --- lib/lp/app/javascript/picker/picker_patcher.js 2012-09-07 14:42:48 +0000 | |||
3 | +++ lib/lp/app/javascript/picker/picker_patcher.js 2016-07-27 17:22:45 +0000 | |||
4 | @@ -1,4 +1,4 @@ | |||
6 | 1 | /* Copyright 2011 Canonical Ltd. This software is licensed under the | 1 | /* Copyright 2011-2016 Canonical Ltd. This software is licensed under the |
7 | 2 | * GNU Affero General Public License version 3 (see the file LICENSE). | 2 | * GNU Affero General Public License version 3 (see the file LICENSE). |
8 | 3 | */ | 3 | */ |
9 | 4 | 4 | ||
10 | @@ -620,7 +620,9 @@ | |||
11 | 620 | // use the context to limit the results to the same project. | 620 | // use the context to limit the results to the same project. |
12 | 621 | 621 | ||
13 | 622 | var uri = ''; | 622 | var uri = ''; |
15 | 623 | if (Y.Lang.isValue(config.context)){ | 623 | if (Y.Lang.isFunction(config.getContextPath)) { |
16 | 624 | uri += config.getContextPath() + '/'; | ||
17 | 625 | } else if (Y.Lang.isValue(config.context)) { | ||
18 | 624 | uri += Y.lp.get_url_path( | 626 | uri += Y.lp.get_url_path( |
19 | 625 | config.context.get('web_link')) + '/'; | 627 | config.context.get('web_link')) + '/'; |
20 | 626 | } | 628 | } |
21 | 627 | 629 | ||
22 | === modified file 'lib/lp/app/javascript/picker/tests/test_picker_patcher.js' | |||
23 | --- lib/lp/app/javascript/picker/tests/test_picker_patcher.js 2013-03-20 03:41:40 +0000 | |||
24 | +++ lib/lp/app/javascript/picker/tests/test_picker_patcher.js 2016-07-27 17:22:45 +0000 | |||
25 | @@ -1,4 +1,4 @@ | |||
27 | 1 | /* Copyright 2012 Canonical Ltd. This software is licensed under the | 1 | /* Copyright 2012-2016 Canonical Ltd. This software is licensed under the |
28 | 2 | * GNU Affero General Public License version 3 (see the file LICENSE). */ | 2 | * GNU Affero General Public License version 3 (see the file LICENSE). */ |
29 | 3 | 3 | ||
30 | 4 | YUI.add('lp.app.picker.test', function (Y) { | 4 | YUI.add('lp.app.picker.test', function (Y) { |
31 | @@ -33,7 +33,7 @@ | |||
32 | 33 | } | 33 | } |
33 | 34 | 34 | ||
34 | 35 | tests.suite.add(new Y.Test.Case({ | 35 | tests.suite.add(new Y.Test.Case({ |
36 | 36 | name: 'picker_yesyno_validation', | 36 | name: 'picker_yesno_validation', |
37 | 37 | 37 | ||
38 | 38 | setUp: function() { | 38 | setUp: function() { |
39 | 39 | this.vocabulary = [ | 39 | this.vocabulary = [ |
40 | @@ -514,6 +514,39 @@ | |||
41 | 514 | 514 | ||
42 | 515 | tests.suite.add(new Y.Test.Case({ | 515 | tests.suite.add(new Y.Test.Case({ |
43 | 516 | 516 | ||
44 | 517 | name: 'picker_custom_context_path', | ||
45 | 518 | |||
46 | 519 | create_picker: function(yio, extra_config) { | ||
47 | 520 | var config = {yio: yio}; | ||
48 | 521 | if (extra_config !== undefined) { | ||
49 | 522 | config = Y.merge(extra_config, config); | ||
50 | 523 | } | ||
51 | 524 | return Y.lp.app.picker.addPickerPatcher( | ||
52 | 525 | "Foo", | ||
53 | 526 | "foo/bar", | ||
54 | 527 | "test_link", | ||
55 | 528 | "picker_id", | ||
56 | 529 | config); | ||
57 | 530 | }, | ||
58 | 531 | |||
59 | 532 | test_custom_context_path_honoured: function() { | ||
60 | 533 | var mock_io = new Y.lp.testing.mockio.MockIo(); | ||
61 | 534 | var extra_config = { | ||
62 | 535 | "getContextPath": function() { return "/gentoo"; } | ||
63 | 536 | }; | ||
64 | 537 | var picker = this.create_picker(mock_io, extra_config); | ||
65 | 538 | picker.fire('search', 'package'); | ||
66 | 539 | Assert.areEqual(1, mock_io.requests.length); | ||
67 | 540 | Assert.areEqual( | ||
68 | 541 | '/gentoo/@@+huge-vocabulary?name=Foo&search_text=package&' + | ||
69 | 542 | 'batch=6&start=0', mock_io.requests[0].url); | ||
70 | 543 | cleanup_widget(picker); | ||
71 | 544 | } | ||
72 | 545 | |||
73 | 546 | })); | ||
74 | 547 | |||
75 | 548 | tests.suite.add(new Y.Test.Case({ | ||
76 | 549 | |||
77 | 517 | name: 'picker_error_handling', | 550 | name: 'picker_error_handling', |
78 | 518 | 551 | ||
79 | 519 | setUp: function() { | 552 | setUp: function() { |
80 | 520 | 553 | ||
81 | === modified file 'lib/lp/app/widgets/popup.py' | |||
82 | --- lib/lp/app/widgets/popup.py 2013-04-10 08:01:20 +0000 | |||
83 | +++ lib/lp/app/widgets/popup.py 2016-07-27 17:22:45 +0000 | |||
84 | @@ -1,4 +1,4 @@ | |||
86 | 1 | # Copyright 2009 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2009-2016 Canonical Ltd. This software is licensed under the |
87 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
88 | 3 | 3 | ||
89 | 4 | """Single selection widget using a popup to select one item from many.""" | 4 | """Single selection widget using a popup to select one item from many.""" |
90 | @@ -275,3 +275,16 @@ | |||
91 | 275 | "looking for? " | 275 | "looking for? " |
92 | 276 | '<a href="%s/+affects-new-product">Register it</a>.</strong>' | 276 | '<a href="%s/+affects-new-product">Register it</a>.</strong>' |
93 | 277 | % canonical_url(self.context.context)) | 277 | % canonical_url(self.context.context)) |
94 | 278 | |||
95 | 279 | |||
96 | 280 | class DistributionSourcePackagePickerWidget(VocabularyPickerWidget): | ||
97 | 281 | """Custom popup widget for choosing distribution/package combinations.""" | ||
98 | 282 | |||
99 | 283 | __call__ = ViewPageTemplateFile( | ||
100 | 284 | 'templates/distributionsourcepackage-picker.pt') | ||
101 | 285 | |||
102 | 286 | @property | ||
103 | 287 | def distribution_id(self): | ||
104 | 288 | return self._prefix + 'distribution' | ||
105 | 289 | |||
106 | 290 | distribution_name = '' | ||
107 | 278 | 291 | ||
108 | === added file 'lib/lp/app/widgets/templates/distributionsourcepackage-picker.pt' | |||
109 | --- lib/lp/app/widgets/templates/distributionsourcepackage-picker.pt 1970-01-01 00:00:00 +0000 | |||
110 | +++ lib/lp/app/widgets/templates/distributionsourcepackage-picker.pt 2016-07-27 17:22:45 +0000 | |||
111 | @@ -0,0 +1,29 @@ | |||
112 | 1 | <tal:root | ||
113 | 2 | xmlns:tal="http://xml.zope.org/namespaces/tal" | ||
114 | 3 | xmlns:metal="http://xml.zope.org/namespaces/metal" | ||
115 | 4 | omit-tag=""> | ||
116 | 5 | |||
117 | 6 | <metal:form-picker use-macro="context/@@form-picker-macros/form-picker"> | ||
118 | 7 | <script metal:fill-slot="add-picker" tal:content="structure string: | ||
119 | 8 | LPJS.use('node', 'lp.app.picker', function(Y) { | ||
120 | 9 | var config = ${view/json_config}; | ||
121 | 10 | var distribution_name = '${view/distribution_name}'; | ||
122 | 11 | var distribution_id = '${view/distribution_id}'; | ||
123 | 12 | if (distribution_name !== '') { | ||
124 | 13 | config.getContextPath = function() { | ||
125 | 14 | return '/' + distribution_name; | ||
126 | 15 | } | ||
127 | 16 | } else if (distribution_id !== '') { | ||
128 | 17 | config.getContextPath = function() { | ||
129 | 18 | return '/' + Y.DOM.byId(distribution_id).value; | ||
130 | 19 | }; | ||
131 | 20 | } | ||
132 | 21 | var show_widget_id = '${view/show_widget_id}'; | ||
133 | 22 | Y.on('domready', function(e) { | ||
134 | 23 | Y.lp.app.picker.addPicker(config, show_widget_id); | ||
135 | 24 | }); | ||
136 | 25 | }); | ||
137 | 26 | "/> | ||
138 | 27 | </metal:form-picker> | ||
139 | 28 | |||
140 | 29 | </tal:root> | ||
141 | 0 | 30 | ||
142 | === modified file 'lib/lp/app/widgets/templates/form-picker-macros.pt' | |||
143 | --- lib/lp/app/widgets/templates/form-picker-macros.pt 2012-02-01 23:47:54 +0000 | |||
144 | +++ lib/lp/app/widgets/templates/form-picker-macros.pt 2016-07-27 17:22:45 +0000 | |||
145 | @@ -29,7 +29,7 @@ | |||
146 | 29 | </tal:search_results> | 29 | </tal:search_results> |
147 | 30 | 30 | ||
148 | 31 | <tal:chooseLink replace="structure view/chooseLink" /> | 31 | <tal:chooseLink replace="structure view/chooseLink" /> |
150 | 32 | <script tal:content="structure string: | 32 | <script metal:define-slot="add-picker" tal:content="structure string: |
151 | 33 | LPJS.use('node', 'lp.app.picker', function(Y) { | 33 | LPJS.use('node', 'lp.app.picker', function(Y) { |
152 | 34 | var config = ${view/json_config}; | 34 | var config = ${view/json_config}; |
153 | 35 | var show_widget_id = '${view/show_widget_id}'; | 35 | var show_widget_id = '${view/show_widget_id}'; |
154 | 36 | 36 | ||
155 | === modified file 'lib/lp/app/widgets/tests/test_launchpadtarget.py' | |||
156 | --- lib/lp/app/widgets/tests/test_launchpadtarget.py 2016-07-11 22:35:59 +0000 | |||
157 | +++ lib/lp/app/widgets/tests/test_launchpadtarget.py 2016-07-27 17:22:45 +0000 | |||
158 | @@ -82,7 +82,7 @@ | |||
159 | 82 | # The render template is setup. | 82 | # The render template is setup. |
160 | 83 | self.assertTrue( | 83 | self.assertTrue( |
161 | 84 | self.widget.template.filename.endswith('launchpad-target.pt'), | 84 | self.widget.template.filename.endswith('launchpad-target.pt'), |
163 | 85 | 'Template was not setup.') | 85 | 'Template was not set up.') |
164 | 86 | 86 | ||
165 | 87 | def test_default_option(self): | 87 | def test_default_option(self): |
166 | 88 | # This package field is the default option. | 88 | # This package field is the default option. |
167 | @@ -198,20 +198,10 @@ | |||
168 | 198 | self.widget.request = LaunchpadTestRequest(form=self.form) | 198 | self.widget.request = LaunchpadTestRequest(form=self.form) |
169 | 199 | self.assertEqual(self.package, self.widget.getInputValue()) | 199 | self.assertEqual(self.package, self.widget.getInputValue()) |
170 | 200 | 200 | ||
171 | 201 | def test_getInputValue_package_spn_dsp_picker_feature_flag(self): | ||
172 | 202 | # The field value is the package when the package radio button | ||
173 | 203 | # is selected and the package sub field has a official dsp. | ||
174 | 204 | self.widget.request = LaunchpadTestRequest(form=self.form) | ||
175 | 205 | with FeatureFixture({u"disclosure.dsp_picker.enabled": u"on"}): | ||
176 | 206 | self.widget.setUpSubWidgets() | ||
177 | 207 | self.assertEqual(self.package, self.widget.getInputValue()) | ||
178 | 208 | |||
179 | 209 | def test_getInputValue_package_dsp_dsp_picker_feature_flag(self): | 201 | def test_getInputValue_package_dsp_dsp_picker_feature_flag(self): |
180 | 210 | # The field value is the package when the package radio button | 202 | # The field value is the package when the package radio button |
181 | 211 | # is selected and the package sub field has valid input. | 203 | # is selected and the package sub field has valid input. |
185 | 212 | form = self.form | 204 | self.widget.request = LaunchpadTestRequest(form=self.form) |
183 | 213 | form['field.target.package'] = 'fnord/snarf' | ||
184 | 214 | self.widget.request = LaunchpadTestRequest(form=form) | ||
186 | 215 | with FeatureFixture({u"disclosure.dsp_picker.enabled": u"on"}): | 205 | with FeatureFixture({u"disclosure.dsp_picker.enabled": u"on"}): |
187 | 216 | self.widget.setUpSubWidgets() | 206 | self.widget.setUpSubWidgets() |
188 | 217 | self.assertEqual(self.package, self.widget.getInputValue()) | 207 | self.assertEqual(self.package, self.widget.getInputValue()) |
189 | 218 | 208 | ||
190 | === modified file 'lib/lp/app/widgets/tests/test_popup.py' | |||
191 | --- lib/lp/app/widgets/tests/test_popup.py 2012-08-28 04:35:13 +0000 | |||
192 | +++ lib/lp/app/widgets/tests/test_popup.py 2016-07-27 17:22:45 +0000 | |||
193 | @@ -1,4 +1,4 @@ | |||
195 | 1 | # Copyright 2010-2011 Canonical Ltd. This software is licensed under the | 1 | # Copyright 2010-2016 Canonical Ltd. This software is licensed under the |
196 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). |
197 | 3 | 3 | ||
198 | 4 | __metaclass__ = type | 4 | __metaclass__ = type |
199 | @@ -10,6 +10,7 @@ | |||
200 | 10 | from zope.schema.vocabulary import getVocabularyRegistry | 10 | from zope.schema.vocabulary import getVocabularyRegistry |
201 | 11 | 11 | ||
202 | 12 | from lp.app.widgets.popup import ( | 12 | from lp.app.widgets.popup import ( |
203 | 13 | DistributionSourcePackagePickerWidget, | ||
204 | 13 | PersonPickerWidget, | 14 | PersonPickerWidget, |
205 | 14 | VocabularyPickerWidget, | 15 | VocabularyPickerWidget, |
206 | 15 | ) | 16 | ) |
207 | @@ -23,12 +24,11 @@ | |||
208 | 23 | def __init__(self, name, bases=(), attrs=None, __doc__=None, | 24 | def __init__(self, name, bases=(), attrs=None, __doc__=None, |
209 | 24 | __module__=None): | 25 | __module__=None): |
210 | 25 | attrs = { | 26 | attrs = { |
217 | 26 | "test_invalid_chars+": | 27 | "test_invalid_chars+": Choice(vocabulary='ValidTeamOwner'), |
218 | 27 | Choice(vocabulary='ValidTeamOwner'), | 28 | "test_valid.item": Choice(vocabulary='ValidTeamOwner'), |
219 | 28 | "test_valid.item": | 29 | "test_filtered.item": Choice(vocabulary='DistributionOrProduct'), |
220 | 29 | Choice(vocabulary='ValidTeamOwner'), | 30 | "test_target": Choice(vocabulary='DistributionSourcePackage'), |
221 | 30 | "test_filtered.item": | 31 | } |
216 | 31 | Choice(vocabulary='DistributionOrProduct')} | ||
222 | 32 | super(TestMetaClass, self).__init__( | 32 | super(TestMetaClass, self).__init__( |
223 | 33 | name, bases=bases, attrs=attrs, __doc__=__doc__, | 33 | name, bases=bases, attrs=attrs, __doc__=__doc__, |
224 | 34 | __module__=__module__) | 34 | __module__=__module__) |
225 | @@ -197,3 +197,29 @@ | |||
226 | 197 | person_picker_widget.setRenderedValue(team) | 197 | person_picker_widget.setRenderedValue(team) |
227 | 198 | self.assertEqual('team', | 198 | self.assertEqual('team', |
228 | 199 | person_picker_widget.config['selected_value_metadata']) | 199 | person_picker_widget.config['selected_value_metadata']) |
229 | 200 | |||
230 | 201 | def test_distribution_source_package_widget_distribution_id(self): | ||
231 | 202 | # The distribution source package picker refers to the correct field | ||
232 | 203 | # in distribution_id. | ||
233 | 204 | field = ITest['test_target'] | ||
234 | 205 | bound_field = field.bind(self.context) | ||
235 | 206 | vocabulary = self.vocabulary_registry.get( | ||
236 | 207 | self.context, 'DistributionSourcePackage') | ||
237 | 208 | dsp_picker_widget = DistributionSourcePackagePickerWidget( | ||
238 | 209 | bound_field, vocabulary, self.request) | ||
239 | 210 | dsp_picker_widget.setPrefix('field.target') | ||
240 | 211 | self.assertEqual( | ||
241 | 212 | 'field.target.distribution', dsp_picker_widget.distribution_id) | ||
242 | 213 | |||
243 | 214 | def test_distribution_source_package_widget_gets_distribution(self): | ||
244 | 215 | # The distribution source package picker gets the value of the | ||
245 | 216 | # distribution field. | ||
246 | 217 | field = ITest['test_target'] | ||
247 | 218 | bound_field = field.bind(self.context) | ||
248 | 219 | vocabulary = self.vocabulary_registry.get( | ||
249 | 220 | self.context, 'DistributionSourcePackage') | ||
250 | 221 | dsp_picker_widget = DistributionSourcePackagePickerWidget( | ||
251 | 222 | bound_field, vocabulary, self.request) | ||
252 | 223 | dsp_picker_widget.setPrefix('field.target') | ||
253 | 224 | markup = dsp_picker_widget() | ||
254 | 225 | self.assertIn("distribution_id = 'field.target.distribution'", markup) | ||
255 | 200 | 226 | ||
256 | === modified file 'lib/lp/bugs/browser/tests/test_bugalsoaffects.py' | |||
257 | --- lib/lp/bugs/browser/tests/test_bugalsoaffects.py 2016-07-11 22:35:59 +0000 | |||
258 | +++ lib/lp/bugs/browser/tests/test_bugalsoaffects.py 2016-07-27 17:22:45 +0000 | |||
259 | @@ -54,24 +54,7 @@ | |||
260 | 54 | browser = self.openBugPage(bug) | 54 | browser = self.openBugPage(bug) |
261 | 55 | browser.getLink(url='+distrotask').click() | 55 | browser.getLink(url='+distrotask').click() |
262 | 56 | browser.getControl('Distribution').value = [self.distribution.name] | 56 | browser.getControl('Distribution').value = [self.distribution.name] |
281 | 57 | browser.getControl('Source Package Name').value = ( | 57 | browser.getControl('Source Package Name').value = dsp.name |
264 | 58 | dsp.sourcepackagename.name) | ||
265 | 59 | browser.getControl('Continue').click() | ||
266 | 60 | self.assertEqual([], get_feedback_messages(browser.contents)) | ||
267 | 61 | |||
268 | 62 | def test_bug_alsoaffects_dsp_exists_dsp_picker_feature_flag(self): | ||
269 | 63 | # If the distribution source package is official, there is no error. | ||
270 | 64 | bug = self.factory.makeBug() | ||
271 | 65 | distroseries = self.factory.makeDistroSeries( | ||
272 | 66 | distribution=self.distribution) | ||
273 | 67 | dsp = self.factory.makeDSPCache( | ||
274 | 68 | distroseries=distroseries, sourcepackagename='snarf') | ||
275 | 69 | with FeatureFixture({u"disclosure.dsp_picker.enabled": u"on"}): | ||
276 | 70 | browser = self.openBugPage(bug) | ||
277 | 71 | browser.getLink(url='+distrotask').click() | ||
278 | 72 | browser.getControl('Distribution').value = [self.distribution.name] | ||
279 | 73 | browser.getControl('Source Package Name').value = ( | ||
280 | 74 | '%s/%s' % (self.distribution.name, dsp.name)) | ||
282 | 75 | browser.getControl('Continue').click() | 58 | browser.getControl('Continue').click() |
283 | 76 | self.assertEqual([], get_feedback_messages(browser.contents)) | 59 | self.assertEqual([], get_feedback_messages(browser.contents)) |
284 | 77 | 60 | ||
285 | 78 | 61 | ||
286 | === modified file 'lib/lp/bugs/browser/widgets/bugtask.py' | |||
287 | --- lib/lp/bugs/browser/widgets/bugtask.py 2016-06-10 22:06:13 +0000 | |||
288 | +++ lib/lp/bugs/browser/widgets/bugtask.py 2016-07-27 17:22:45 +0000 | |||
289 | @@ -53,8 +53,8 @@ | |||
290 | 53 | from lp.app.widgets.helpers import get_widget_template | 53 | from lp.app.widgets.helpers import get_widget_template |
291 | 54 | from lp.app.widgets.launchpadtarget import LaunchpadTargetWidget | 54 | from lp.app.widgets.launchpadtarget import LaunchpadTargetWidget |
292 | 55 | from lp.app.widgets.popup import ( | 55 | from lp.app.widgets.popup import ( |
293 | 56 | DistributionSourcePackagePickerWidget, | ||
294 | 56 | PersonPickerWidget, | 57 | PersonPickerWidget, |
295 | 57 | VocabularyPickerWidget, | ||
296 | 58 | ) | 58 | ) |
297 | 59 | from lp.app.widgets.textwidgets import ( | 59 | from lp.app.widgets.textwidgets import ( |
298 | 60 | StrippedTextWidget, | 60 | StrippedTextWidget, |
299 | @@ -478,12 +478,17 @@ | |||
300 | 478 | return vocabulary | 478 | return vocabulary |
301 | 479 | 479 | ||
302 | 480 | 480 | ||
304 | 481 | class BugTaskSourcePackageNameWidget(VocabularyPickerWidget): | 481 | class BugTaskSourcePackageNameWidget(DistributionSourcePackagePickerWidget): |
305 | 482 | """A widget for associating a bugtask with a SourcePackageName. | 482 | """A widget for associating a bugtask with a SourcePackageName. |
306 | 483 | 483 | ||
307 | 484 | It accepts both binary and source package names. | 484 | It accepts both binary and source package names. |
308 | 485 | """ | 485 | """ |
309 | 486 | 486 | ||
310 | 487 | # Pages that use this widget don't display the distribution, but this | ||
311 | 488 | # can only be used by bugtasks on the distribution in question so the | ||
312 | 489 | # vocabulary will be able to work it out for itself. | ||
313 | 490 | distribution_id = '' | ||
314 | 491 | |||
315 | 487 | def __init__(self, field, vocabulary, request): | 492 | def __init__(self, field, vocabulary, request): |
316 | 488 | super(BugTaskSourcePackageNameWidget, self).__init__( | 493 | super(BugTaskSourcePackageNameWidget, self).__init__( |
317 | 489 | field, vocabulary, request) | 494 | field, vocabulary, request) |
318 | @@ -542,6 +547,8 @@ | |||
319 | 542 | except that it gets the distribution from the request. | 547 | except that it gets the distribution from the request. |
320 | 543 | """ | 548 | """ |
321 | 544 | 549 | ||
322 | 550 | distribution_id = 'field.distribution' | ||
323 | 551 | |||
324 | 545 | def getDistribution(self): | 552 | def getDistribution(self): |
325 | 546 | """See `BugTaskSourcePackageNameWidget`""" | 553 | """See `BugTaskSourcePackageNameWidget`""" |
326 | 547 | distribution_name = self.request.form.get('field.distribution') | 554 | distribution_name = self.request.form.get('field.distribution') |
327 | @@ -559,6 +566,8 @@ | |||
328 | 559 | class UbuntuSourcePackageNameWidget(BugTaskSourcePackageNameWidget): | 566 | class UbuntuSourcePackageNameWidget(BugTaskSourcePackageNameWidget): |
329 | 560 | """A widget to select Ubuntu packages.""" | 567 | """A widget to select Ubuntu packages.""" |
330 | 561 | 568 | ||
331 | 569 | distribution_name = 'ubuntu' | ||
332 | 570 | |||
333 | 562 | def getDistribution(self): | 571 | def getDistribution(self): |
334 | 563 | """See `BugTaskSourcePackageNameWidget`""" | 572 | """See `BugTaskSourcePackageNameWidget`""" |
335 | 564 | return getUtility(ILaunchpadCelebrities).ubuntu | 573 | return getUtility(ILaunchpadCelebrities).ubuntu |
336 | 565 | 574 | ||
337 | === modified file 'lib/lp/registry/tests/test_distributionsourcepackage_vocabulary.py' | |||
338 | --- lib/lp/registry/tests/test_distributionsourcepackage_vocabulary.py 2016-07-11 22:35:59 +0000 | |||
339 | +++ lib/lp/registry/tests/test_distributionsourcepackage_vocabulary.py 2016-07-27 17:22:45 +0000 | |||
340 | @@ -69,52 +69,24 @@ | |||
341 | 69 | vocabulary.setDistribution(new_distro) | 69 | vocabulary.setDistribution(new_distro) |
342 | 70 | self.assertEqual(new_distro, vocabulary.distribution) | 70 | self.assertEqual(new_distro, vocabulary.distribution) |
343 | 71 | 71 | ||
373 | 72 | def test_parseToken_distro_and_package(self): | 72 | def test_contains_true_with_distribution(self): |
345 | 73 | # parseToken returns a tuple of distribution and package name when | ||
346 | 74 | # the text contains both. | ||
347 | 75 | new_distro = self.factory.makeDistribution(name='fnord') | ||
348 | 76 | vocabulary = DistributionSourcePackageVocabulary(None) | ||
349 | 77 | distribution, package_name = vocabulary.parseToken('fnord/pting') | ||
350 | 78 | self.assertEqual(new_distro, distribution) | ||
351 | 79 | self.assertEqual('pting', package_name) | ||
352 | 80 | |||
353 | 81 | def test_parseToken_default_distro_and_package(self): | ||
354 | 82 | # parseToken returns a tuple of the default distribution and package | ||
355 | 83 | # name when the text is just a package name. | ||
356 | 84 | default_distro = self.factory.makeDistribution(name='fnord') | ||
357 | 85 | vocabulary = DistributionSourcePackageVocabulary(default_distro) | ||
358 | 86 | distribution, package_name = vocabulary.parseToken('pting') | ||
359 | 87 | self.assertEqual(default_distro, distribution) | ||
360 | 88 | self.assertEqual('pting', package_name) | ||
361 | 89 | |||
362 | 90 | def test_parseToken_bad_distro_and_package(self): | ||
363 | 91 | # parseToken returns a tuple of the default distribution and package | ||
364 | 92 | # name when the distro in the text cannot be matched to a real | ||
365 | 93 | # distro. | ||
366 | 94 | default_distro = self.factory.makeDistribution(name='fnord') | ||
367 | 95 | vocabulary = DistributionSourcePackageVocabulary(default_distro) | ||
368 | 96 | distribution, package_name = vocabulary.parseToken('misspelled/pting') | ||
369 | 97 | self.assertEqual(default_distro, distribution) | ||
370 | 98 | self.assertEqual('pting', package_name) | ||
371 | 99 | |||
372 | 100 | def test_contains_true_without_init(self): | ||
374 | 101 | # The vocabulary contains official DSPs. | 73 | # The vocabulary contains official DSPs. |
375 | 102 | dsp = self.factory.makeDistributionSourcePackage(with_db=True) | 74 | dsp = self.factory.makeDistributionSourcePackage(with_db=True) |
377 | 103 | vocabulary = DistributionSourcePackageVocabulary(None) | 75 | vocabulary = DistributionSourcePackageVocabulary(dsp.distribution) |
378 | 104 | self.assertIn(dsp, vocabulary) | 76 | self.assertIn(dsp, vocabulary) |
379 | 105 | 77 | ||
381 | 106 | def test_contains_true_with_init(self): | 78 | def test_contains_true_with_dsp(self): |
382 | 107 | # The vocabulary contains the DSP passed to init when it is not | 79 | # The vocabulary contains the DSP passed to init when it is not |
383 | 108 | # official. | 80 | # official. |
384 | 109 | dsp = self.factory.makeDistributionSourcePackage(with_db=False) | 81 | dsp = self.factory.makeDistributionSourcePackage(with_db=False) |
385 | 110 | vocabulary = DistributionSourcePackageVocabulary(dsp) | 82 | vocabulary = DistributionSourcePackageVocabulary(dsp) |
386 | 111 | self.assertIn(dsp, vocabulary) | 83 | self.assertIn(dsp, vocabulary) |
387 | 112 | 84 | ||
389 | 113 | def test_contains_false_without_init(self): | 85 | def test_contains_false_with_distribution(self): |
390 | 114 | # The vocabulary does not contain DSPs that are not official that | 86 | # The vocabulary does not contain DSPs that are not official that |
391 | 115 | # were not passed to init. | 87 | # were not passed to init. |
392 | 116 | dsp = self.factory.makeDistributionSourcePackage(with_db=False) | 88 | dsp = self.factory.makeDistributionSourcePackage(with_db=False) |
394 | 117 | vocabulary = DistributionSourcePackageVocabulary(None) | 89 | vocabulary = DistributionSourcePackageVocabulary(dsp.distribution) |
395 | 118 | self.assertNotIn(dsp, vocabulary) | 90 | self.assertNotIn(dsp, vocabulary) |
396 | 119 | 91 | ||
397 | 120 | def test_toTerm_raises_error(self): | 92 | def test_toTerm_raises_error(self): |
398 | @@ -123,11 +95,12 @@ | |||
399 | 123 | dsp = self.factory.makeDistributionSourcePackage( | 95 | dsp = self.factory.makeDistributionSourcePackage( |
400 | 124 | sourcepackagename='foo') | 96 | sourcepackagename='foo') |
401 | 125 | vocabulary = DistributionSourcePackageVocabulary(dsp.distribution) | 97 | vocabulary = DistributionSourcePackageVocabulary(dsp.distribution) |
403 | 126 | self.assertRaises(LookupError, vocabulary.toTerm, dsp.name) | 98 | self.assertRaises(LookupError, vocabulary.toTerm, dsp) |
404 | 127 | 99 | ||
405 | 128 | def test_toTerm_none_raises_error(self): | 100 | def test_toTerm_none_raises_error(self): |
406 | 129 | # An error is raised for an SPN that does not exist. | 101 | # An error is raised for an SPN that does not exist. |
408 | 130 | vocabulary = DistributionSourcePackageVocabulary(None) | 102 | vocabulary = DistributionSourcePackageVocabulary( |
409 | 103 | self.factory.makeDistribution()) | ||
410 | 131 | self.assertRaises(LookupError, vocabulary.toTerm, 'nonexistent') | 104 | self.assertRaises(LookupError, vocabulary.toTerm, 'nonexistent') |
411 | 132 | 105 | ||
412 | 133 | def test_toTerm_spn_and_default_distribution(self): | 106 | def test_toTerm_spn_and_default_distribution(self): |
413 | @@ -137,21 +110,8 @@ | |||
414 | 137 | spph.sourcepackagerelease.sourcepackagename) | 110 | spph.sourcepackagerelease.sourcepackagename) |
415 | 138 | vocabulary = DistributionSourcePackageVocabulary(dsp.distribution) | 111 | vocabulary = DistributionSourcePackageVocabulary(dsp.distribution) |
416 | 139 | term = vocabulary.toTerm(dsp.sourcepackagename) | 112 | term = vocabulary.toTerm(dsp.sourcepackagename) |
432 | 140 | expected_token = '%s/%s' % (dsp.distribution.name, dsp.name) | 113 | self.assertEqual(dsp.name, term.token) |
433 | 141 | self.assertEqual(expected_token, term.token) | 114 | self.assertEqual(dsp.name, term.title) |
419 | 142 | self.assertEqual(expected_token, term.title) | ||
420 | 143 | self.assertEqual(dsp, term.value) | ||
421 | 144 | |||
422 | 145 | def test_toTerm_spn_and_distribution(self): | ||
423 | 146 | # The distribution is used with the spn if it is passed. | ||
424 | 147 | spph = self.factory.makeSourcePackagePublishingHistory() | ||
425 | 148 | dsp = spph.distroseries.distribution.getSourcePackage( | ||
426 | 149 | spph.sourcepackagerelease.sourcepackagename) | ||
427 | 150 | vocabulary = DistributionSourcePackageVocabulary(None) | ||
428 | 151 | term = vocabulary.toTerm(dsp.sourcepackagename, dsp.distribution) | ||
429 | 152 | expected_token = '%s/%s' % (dsp.distribution.name, dsp.name) | ||
430 | 153 | self.assertEqual(expected_token, term.token) | ||
431 | 154 | self.assertEqual(expected_token, term.title) | ||
434 | 155 | self.assertEqual(dsp, term.value) | 115 | self.assertEqual(dsp, term.value) |
435 | 156 | 116 | ||
436 | 157 | def test_toTerm_dsp(self): | 117 | def test_toTerm_dsp(self): |
437 | @@ -161,9 +121,8 @@ | |||
438 | 161 | spph.sourcepackagerelease.sourcepackagename) | 121 | spph.sourcepackagerelease.sourcepackagename) |
439 | 162 | vocabulary = DistributionSourcePackageVocabulary(dsp) | 122 | vocabulary = DistributionSourcePackageVocabulary(dsp) |
440 | 163 | term = vocabulary.toTerm(dsp) | 123 | term = vocabulary.toTerm(dsp) |
444 | 164 | expected_token = '%s/%s' % (dsp.distribution.name, dsp.name) | 124 | self.assertEqual(dsp.name, term.token) |
445 | 165 | self.assertEqual(expected_token, term.token) | 125 | self.assertEqual(dsp.name, term.title) |
443 | 166 | self.assertEqual(expected_token, term.title) | ||
446 | 167 | self.assertEqual(dsp, term.value) | 126 | self.assertEqual(dsp, term.value) |
447 | 168 | 127 | ||
448 | 169 | def test_toTerm_dsp_and_binary_names(self): | 128 | def test_toTerm_dsp_and_binary_names(self): |
449 | @@ -174,9 +133,8 @@ | |||
450 | 174 | spph.sourcepackagerelease.sourcepackagename) | 133 | spph.sourcepackagerelease.sourcepackagename) |
451 | 175 | vocabulary = DistributionSourcePackageVocabulary(dsp) | 134 | vocabulary = DistributionSourcePackageVocabulary(dsp) |
452 | 176 | term = vocabulary.toTerm((dsp, 'one two')) | 135 | term = vocabulary.toTerm((dsp, 'one two')) |
456 | 177 | expected_token = '%s/%s' % (dsp.distribution.name, dsp.name) | 136 | self.assertEqual(dsp.name, term.token) |
457 | 178 | self.assertEqual(expected_token, term.token) | 137 | self.assertEqual(dsp.name, term.title) |
455 | 179 | self.assertEqual(expected_token, term.title) | ||
458 | 180 | self.assertEqual(dsp, term.value) | 138 | self.assertEqual(dsp, term.value) |
459 | 181 | self.assertEqual(['one', 'two'], term.value.binary_names) | 139 | self.assertEqual(['one', 'two'], term.value.binary_names) |
460 | 182 | 140 | ||
461 | @@ -185,8 +143,7 @@ | |||
462 | 185 | dsp = self.factory.makeDistributionSourcePackage( | 143 | dsp = self.factory.makeDistributionSourcePackage( |
463 | 186 | sourcepackagename='foo') | 144 | sourcepackagename='foo') |
464 | 187 | vocabulary = DistributionSourcePackageVocabulary(dsp.distribution) | 145 | vocabulary = DistributionSourcePackageVocabulary(dsp.distribution) |
467 | 188 | token = '%s/%s' % (dsp.distribution.name, dsp.name) | 146 | self.assertRaises(LookupError, vocabulary.getTermByToken, dsp.name) |
466 | 189 | self.assertRaises(LookupError, vocabulary.getTermByToken, token) | ||
468 | 190 | 147 | ||
469 | 191 | def test_getTermByToken_token(self): | 148 | def test_getTermByToken_token(self): |
470 | 192 | # The term is returned if it matches an official DSP. | 149 | # The term is returned if it matches an official DSP. |
471 | @@ -194,20 +151,16 @@ | |||
472 | 194 | dsp = spph.distroseries.distribution.getSourcePackage( | 151 | dsp = spph.distroseries.distribution.getSourcePackage( |
473 | 195 | spph.sourcepackagerelease.sourcepackagename) | 152 | spph.sourcepackagerelease.sourcepackagename) |
474 | 196 | vocabulary = DistributionSourcePackageVocabulary(dsp.distribution) | 153 | vocabulary = DistributionSourcePackageVocabulary(dsp.distribution) |
477 | 197 | token = '%s/%s' % (dsp.distribution.name, dsp.name) | 154 | term = vocabulary.getTermByToken(dsp.name) |
476 | 198 | term = vocabulary.getTermByToken(token) | ||
478 | 199 | self.assertEqual(dsp, term.value) | 155 | self.assertEqual(dsp, term.value) |
479 | 200 | 156 | ||
480 | 201 | def test_searchForTerms_without_distribution(self): | 157 | def test_searchForTerms_without_distribution(self): |
484 | 202 | # An empty result set is returned if the vocabulary has no | 158 | # searchForTerms asserts that the vocabulary has a distribution. |
482 | 203 | # distribution and the search does not provide distribution | ||
483 | 204 | # information. | ||
485 | 205 | spph = self.factory.makeSourcePackagePublishingHistory() | 159 | spph = self.factory.makeSourcePackagePublishingHistory() |
486 | 206 | dsp = spph.distroseries.distribution.getSourcePackage( | 160 | dsp = spph.distroseries.distribution.getSourcePackage( |
487 | 207 | spph.sourcepackagerelease.sourcepackagename) | 161 | spph.sourcepackagerelease.sourcepackagename) |
488 | 208 | vocabulary = DistributionSourcePackageVocabulary(dsp.name) | 162 | vocabulary = DistributionSourcePackageVocabulary(dsp.name) |
491 | 209 | results = vocabulary.searchForTerms(dsp.name) | 163 | self.assertRaises(AssertionError, vocabulary.searchForTerms, dsp.name) |
490 | 210 | self.assertEqual(0, results.count()) | ||
492 | 211 | 164 | ||
493 | 212 | def test_searchForTerms_None(self): | 165 | def test_searchForTerms_None(self): |
494 | 213 | # Searching for nothing gets you that. | 166 | # Searching for nothing gets you that. |
495 | @@ -222,11 +175,11 @@ | |||
496 | 222 | distroseries = self.factory.makeDistroSeries(distribution=distro) | 175 | distroseries = self.factory.makeDistroSeries(distribution=distro) |
497 | 223 | self.factory.makeDSPCache( | 176 | self.factory.makeDSPCache( |
498 | 224 | distroseries=distroseries, sourcepackagename='snarf') | 177 | distroseries=distroseries, sourcepackagename='snarf') |
501 | 225 | vocabulary = DistributionSourcePackageVocabulary(None) | 178 | vocabulary = DistributionSourcePackageVocabulary(distro) |
502 | 226 | results = vocabulary.searchForTerms(query='fnord/snarf') | 179 | results = vocabulary.searchForTerms(query='snarf') |
503 | 227 | terms = list(results) | 180 | terms = list(results) |
504 | 228 | self.assertEqual(1, len(terms)) | 181 | self.assertEqual(1, len(terms)) |
506 | 229 | self.assertEqual('fnord/snarf', terms[0].token) | 182 | self.assertEqual('snarf', terms[0].token) |
507 | 230 | 183 | ||
508 | 231 | def test_searchForTerms_similar_official_source_name(self): | 184 | def test_searchForTerms_similar_official_source_name(self): |
509 | 232 | # Partial source name matches are found. | 185 | # Partial source name matches are found. |
510 | @@ -234,11 +187,11 @@ | |||
511 | 234 | distroseries = self.factory.makeDistroSeries(distribution=distro) | 187 | distroseries = self.factory.makeDistroSeries(distribution=distro) |
512 | 235 | self.factory.makeDSPCache( | 188 | self.factory.makeDSPCache( |
513 | 236 | distroseries=distroseries, sourcepackagename='pting-snarf-ack') | 189 | distroseries=distroseries, sourcepackagename='pting-snarf-ack') |
516 | 237 | vocabulary = DistributionSourcePackageVocabulary(None) | 190 | vocabulary = DistributionSourcePackageVocabulary(distro) |
517 | 238 | results = vocabulary.searchForTerms(query='fnord/snarf') | 191 | results = vocabulary.searchForTerms(query='snarf') |
518 | 239 | terms = list(results) | 192 | terms = list(results) |
519 | 240 | self.assertEqual(1, len(terms)) | 193 | self.assertEqual(1, len(terms)) |
521 | 241 | self.assertEqual('fnord/pting-snarf-ack', terms[0].token) | 194 | self.assertEqual('pting-snarf-ack', terms[0].token) |
522 | 242 | 195 | ||
523 | 243 | def test_searchForTerms_exact_binary_name(self): | 196 | def test_searchForTerms_exact_binary_name(self): |
524 | 244 | # Exact binary name matches are found. | 197 | # Exact binary name matches are found. |
525 | @@ -247,11 +200,11 @@ | |||
526 | 247 | self.factory.makeDSPCache( | 200 | self.factory.makeDSPCache( |
527 | 248 | distroseries=distroseries, sourcepackagename='snarf', | 201 | distroseries=distroseries, sourcepackagename='snarf', |
528 | 249 | binary_names='pting-dev pting ack') | 202 | binary_names='pting-dev pting ack') |
531 | 250 | vocabulary = DistributionSourcePackageVocabulary(None) | 203 | vocabulary = DistributionSourcePackageVocabulary(distro) |
532 | 251 | results = vocabulary.searchForTerms(query='fnord/pting') | 204 | results = vocabulary.searchForTerms(query='pting') |
533 | 252 | terms = list(results) | 205 | terms = list(results) |
534 | 253 | self.assertEqual(1, len(terms)) | 206 | self.assertEqual(1, len(terms)) |
536 | 254 | self.assertEqual('fnord/snarf', terms[0].token) | 207 | self.assertEqual('snarf', terms[0].token) |
537 | 255 | 208 | ||
538 | 256 | def test_searchForTerms_similar_binary_name(self): | 209 | def test_searchForTerms_similar_binary_name(self): |
539 | 257 | # Partial binary name matches are found. | 210 | # Partial binary name matches are found. |
540 | @@ -260,11 +213,11 @@ | |||
541 | 260 | self.factory.makeDSPCache( | 213 | self.factory.makeDSPCache( |
542 | 261 | distroseries=distroseries, sourcepackagename='snarf', | 214 | distroseries=distroseries, sourcepackagename='snarf', |
543 | 262 | binary_names='thrpp pting-dev ack') | 215 | binary_names='thrpp pting-dev ack') |
546 | 263 | vocabulary = DistributionSourcePackageVocabulary(None) | 216 | vocabulary = DistributionSourcePackageVocabulary(distro) |
547 | 264 | results = vocabulary.searchForTerms(query='fnord/pting') | 217 | results = vocabulary.searchForTerms(query='pting') |
548 | 265 | terms = list(results) | 218 | terms = list(results) |
549 | 266 | self.assertEqual(1, len(terms)) | 219 | self.assertEqual(1, len(terms)) |
551 | 267 | self.assertEqual('fnord/snarf', terms[0].token) | 220 | self.assertEqual('snarf', terms[0].token) |
552 | 268 | 221 | ||
553 | 269 | def test_searchForTerms_exact_unofficial_source_name(self): | 222 | def test_searchForTerms_exact_unofficial_source_name(self): |
554 | 270 | # Unofficial source packages are not found by search. | 223 | # Unofficial source packages are not found by search. |
555 | @@ -273,8 +226,8 @@ | |||
556 | 273 | self.factory.makeDSPCache( | 226 | self.factory.makeDSPCache( |
557 | 274 | distroseries=distroseries, sourcepackagename='snarf', | 227 | distroseries=distroseries, sourcepackagename='snarf', |
558 | 275 | official=False) | 228 | official=False) |
561 | 276 | vocabulary = DistributionSourcePackageVocabulary(None) | 229 | vocabulary = DistributionSourcePackageVocabulary(distro) |
562 | 277 | results = vocabulary.searchForTerms(query='fnord/snarf') | 230 | results = vocabulary.searchForTerms(query='snarf') |
563 | 278 | terms = list(results) | 231 | terms = list(results) |
564 | 279 | self.assertEqual(0, len(terms)) | 232 | self.assertEqual(0, len(terms)) |
565 | 280 | 233 | ||
566 | @@ -285,8 +238,8 @@ | |||
567 | 285 | self.factory.makeDSPCache( | 238 | self.factory.makeDSPCache( |
568 | 286 | distroseries=distroseries, sourcepackagename='snarf', | 239 | distroseries=distroseries, sourcepackagename='snarf', |
569 | 287 | official=False, binary_names='thrpp pting ack') | 240 | official=False, binary_names='thrpp pting ack') |
572 | 288 | vocabulary = DistributionSourcePackageVocabulary(None) | 241 | vocabulary = DistributionSourcePackageVocabulary(distro) |
573 | 289 | results = vocabulary.searchForTerms(query='fnord/pting') | 242 | results = vocabulary.searchForTerms(query='pting') |
574 | 290 | terms = list(results) | 243 | terms = list(results) |
575 | 291 | self.assertEqual(0, len(terms)) | 244 | self.assertEqual(0, len(terms)) |
576 | 292 | 245 | ||
577 | @@ -304,14 +257,14 @@ | |||
578 | 304 | self.factory.makeDSPCache( | 257 | self.factory.makeDSPCache( |
579 | 305 | distroseries=distroseries, sourcepackagename='pting-client', | 258 | distroseries=distroseries, sourcepackagename='pting-client', |
580 | 306 | binary_names='snarf-common') | 259 | binary_names='snarf-common') |
583 | 307 | vocabulary = DistributionSourcePackageVocabulary(None) | 260 | vocabulary = DistributionSourcePackageVocabulary(distro) |
584 | 308 | results = vocabulary.searchForTerms(query='fnord/snarf') | 261 | results = vocabulary.searchForTerms(query='snarf') |
585 | 309 | terms = list(results) | 262 | terms = list(results) |
586 | 310 | self.assertEqual(4, len(terms)) | 263 | self.assertEqual(4, len(terms)) |
591 | 311 | self.assertEqual('fnord/snarf', terms[0].token) | 264 | self.assertEqual('snarf', terms[0].token) |
592 | 312 | self.assertEqual('fnord/pting-devel', terms[1].token) | 265 | self.assertEqual('pting-devel', terms[1].token) |
593 | 313 | self.assertEqual('fnord/snarf-server', terms[2].token) | 266 | self.assertEqual('snarf-server', terms[2].token) |
594 | 314 | self.assertEqual('fnord/pting-client', terms[3].token) | 267 | self.assertEqual('pting-client', terms[3].token) |
595 | 315 | 268 | ||
596 | 316 | def test_searchForTerms_partner_archive(self): | 269 | def test_searchForTerms_partner_archive(self): |
597 | 317 | # Packages in partner archives are searched. | 270 | # Packages in partner archives are searched. |
598 | @@ -321,11 +274,11 @@ | |||
599 | 321 | distroseries=distroseries, sourcepackagename='snarf', | 274 | distroseries=distroseries, sourcepackagename='snarf', |
600 | 322 | archive=self.factory.makeArchive( | 275 | archive=self.factory.makeArchive( |
601 | 323 | distribution=distro, purpose=ArchivePurpose.PARTNER)) | 276 | distribution=distro, purpose=ArchivePurpose.PARTNER)) |
604 | 324 | vocabulary = DistributionSourcePackageVocabulary(None) | 277 | vocabulary = DistributionSourcePackageVocabulary(distro) |
605 | 325 | results = vocabulary.searchForTerms(query='fnord/snarf') | 278 | results = vocabulary.searchForTerms(query='snarf') |
606 | 326 | terms = list(results) | 279 | terms = list(results) |
607 | 327 | self.assertEqual(1, len(terms)) | 280 | self.assertEqual(1, len(terms)) |
609 | 328 | self.assertEqual('fnord/snarf', terms[0].token) | 281 | self.assertEqual('snarf', terms[0].token) |
610 | 329 | 282 | ||
611 | 330 | def test_searchForTerms_ppa_archive(self): | 283 | def test_searchForTerms_ppa_archive(self): |
612 | 331 | # Packages in PPAs are ignored. | 284 | # Packages in PPAs are ignored. |
613 | @@ -336,6 +289,6 @@ | |||
614 | 336 | official=False, | 289 | official=False, |
615 | 337 | archive=self.factory.makeArchive( | 290 | archive=self.factory.makeArchive( |
616 | 338 | distribution=distro, purpose=ArchivePurpose.PPA)) | 291 | distribution=distro, purpose=ArchivePurpose.PPA)) |
619 | 339 | vocabulary = DistributionSourcePackageVocabulary(None) | 292 | vocabulary = DistributionSourcePackageVocabulary(distro) |
620 | 340 | results = vocabulary.searchForTerms(query='fnord/snarf') | 293 | results = vocabulary.searchForTerms(query='snarf') |
621 | 341 | self.assertEqual(0, results.count()) | 294 | self.assertEqual(0, results.count()) |
622 | 342 | 295 | ||
623 | === modified file 'lib/lp/registry/vocabularies.py' | |||
624 | --- lib/lp/registry/vocabularies.py 2016-07-11 21:46:03 +0000 | |||
625 | +++ lib/lp/registry/vocabularies.py 2016-07-27 17:22:45 +0000 | |||
626 | @@ -111,10 +111,7 @@ | |||
627 | 111 | PersonVisibility, | 111 | PersonVisibility, |
628 | 112 | ) | 112 | ) |
629 | 113 | from lp.registry.interfaces.accesspolicy import IAccessPolicySource | 113 | from lp.registry.interfaces.accesspolicy import IAccessPolicySource |
634 | 114 | from lp.registry.interfaces.distribution import ( | 114 | from lp.registry.interfaces.distribution import IDistribution |
631 | 115 | IDistribution, | ||
632 | 116 | IDistributionSet, | ||
633 | 117 | ) | ||
635 | 118 | from lp.registry.interfaces.distributionsourcepackage import ( | 115 | from lp.registry.interfaces.distributionsourcepackage import ( |
636 | 119 | IDistributionSourcePackage, | 116 | IDistributionSourcePackage, |
637 | 120 | ) | 117 | ) |
638 | @@ -2028,7 +2025,7 @@ | |||
639 | 2028 | class DistributionSourcePackageVocabulary(FilteredVocabularyBase): | 2025 | class DistributionSourcePackageVocabulary(FilteredVocabularyBase): |
640 | 2029 | 2026 | ||
641 | 2030 | displayname = 'Select a package' | 2027 | displayname = 'Select a package' |
643 | 2031 | step_title = 'Search by name or distro/name' | 2028 | step_title = 'Search by name' |
644 | 2032 | 2029 | ||
645 | 2033 | def __init__(self, context): | 2030 | def __init__(self, context): |
646 | 2034 | self.context = context | 2031 | self.context = context |
647 | @@ -2068,19 +2065,15 @@ | |||
648 | 2068 | """Set the distribution after the vocabulary was instantiated.""" | 2065 | """Set the distribution after the vocabulary was instantiated.""" |
649 | 2069 | self.distribution = distribution | 2066 | self.distribution = distribution |
650 | 2070 | 2067 | ||
661 | 2071 | def parseToken(self, text): | 2068 | def _assertHasDistribution(self): |
662 | 2072 | """Return the distribution and package name from the parsed token.""" | 2069 | if self.distribution is None: |
663 | 2073 | # Match the toTerm() format, but also use it to select a distribution. | 2070 | raise AssertionError( |
664 | 2074 | distribution = None | 2071 | "DistributionSourcePackageVocabulary cannot be used without " |
665 | 2075 | if '/' in text: | 2072 | "setting a distribution.") |
656 | 2076 | distro_name, text = text.split('/', 1) | ||
657 | 2077 | distribution = getUtility(IDistributionSet).getByName(distro_name) | ||
658 | 2078 | if distribution is None: | ||
659 | 2079 | distribution = self.distribution | ||
660 | 2080 | return distribution, text | ||
666 | 2081 | 2073 | ||
668 | 2082 | def toTerm(self, spn_or_dsp, distribution=None): | 2074 | def toTerm(self, spn_or_dsp): |
669 | 2083 | """See `IVocabulary`.""" | 2075 | """See `IVocabulary`.""" |
670 | 2076 | self._assertHasDistribution() | ||
671 | 2084 | dsp = None | 2077 | dsp = None |
672 | 2085 | binary_names = None | 2078 | binary_names = None |
673 | 2086 | if isinstance(spn_or_dsp, tuple): | 2079 | if isinstance(spn_or_dsp, tuple): |
674 | @@ -2088,21 +2081,27 @@ | |||
675 | 2088 | spn_or_dsp, binary_names = spn_or_dsp | 2081 | spn_or_dsp, binary_names = spn_or_dsp |
676 | 2089 | if binary_names is not None: | 2082 | if binary_names is not None: |
677 | 2090 | binary_names = binary_names.split() | 2083 | binary_names = binary_names.split() |
678 | 2084 | # XXX cjwatson 2016-07-27: Eventually this should just take a DSP | ||
679 | 2085 | # and drop the complication of also accepting SPNs; but, for now, | ||
680 | 2086 | # accepting an SPN reduces the amount of feature-flag checks | ||
681 | 2087 | # required by users of this vocabulary. | ||
682 | 2091 | if IDistributionSourcePackage.providedBy(spn_or_dsp): | 2088 | if IDistributionSourcePackage.providedBy(spn_or_dsp): |
683 | 2092 | dsp = spn_or_dsp | 2089 | dsp = spn_or_dsp |
689 | 2093 | distribution = spn_or_dsp.distribution | 2090 | elif spn_or_dsp is not None: |
690 | 2094 | else: | 2091 | dsp = self.distribution.getSourcePackage(spn_or_dsp) |
686 | 2095 | distribution = distribution or self.distribution | ||
687 | 2096 | if distribution is not None and spn_or_dsp is not None: | ||
688 | 2097 | dsp = distribution.getSourcePackage(spn_or_dsp) | ||
691 | 2098 | if dsp is not None and (dsp == self.dsp or dsp.is_official): | 2092 | if dsp is not None and (dsp == self.dsp or dsp.is_official): |
692 | 2099 | if binary_names: | 2093 | if binary_names: |
693 | 2100 | # Search already did the hard work of looking up binary names. | 2094 | # Search already did the hard work of looking up binary names. |
694 | 2101 | cache = get_property_cache(dsp) | 2095 | cache = get_property_cache(dsp) |
695 | 2102 | cache.binary_names = binary_names | 2096 | cache.binary_names = binary_names |
699 | 2103 | token = '%s/%s' % (dsp.distribution.name, dsp.name) | 2097 | # XXX cjwatson 2016-07-22: It's a bit odd for the token to |
700 | 2104 | return SimpleTerm(dsp, token, token) | 2098 | # return just the source package name and not the distribution |
701 | 2105 | raise LookupError(distribution, spn_or_dsp) | 2099 | # name as well, but at the moment this is always fed into a |
702 | 2100 | # package name box so things work much better this way. If we | ||
703 | 2101 | # ever do a true combined distribution/package picker, then this | ||
704 | 2102 | # may need to be revisited. | ||
705 | 2103 | return SimpleTerm(dsp, dsp.name, dsp.name) | ||
706 | 2104 | raise LookupError(self.distribution, spn_or_dsp) | ||
707 | 2106 | 2105 | ||
708 | 2107 | def getTerm(self, spn_or_dsp): | 2106 | def getTerm(self, spn_or_dsp): |
709 | 2108 | """See `IBaseVocabulary`.""" | 2107 | """See `IBaseVocabulary`.""" |
710 | @@ -2110,19 +2109,13 @@ | |||
711 | 2110 | 2109 | ||
712 | 2111 | def getTermByToken(self, token): | 2110 | def getTermByToken(self, token): |
713 | 2112 | """See `IVocabularyTokenized`.""" | 2111 | """See `IVocabularyTokenized`.""" |
716 | 2113 | distribution, package_name = self.parseToken(token) | 2112 | return self.toTerm(token) |
715 | 2114 | return self.toTerm(package_name, distribution) | ||
717 | 2115 | 2113 | ||
718 | 2116 | def searchForTerms(self, query=None, vocab_filter=None): | 2114 | def searchForTerms(self, query=None, vocab_filter=None): |
719 | 2117 | """See `IHugeVocabulary`.""" | 2115 | """See `IHugeVocabulary`.""" |
720 | 2116 | self._assertHasDistribution() | ||
721 | 2118 | if not query: | 2117 | if not query: |
722 | 2119 | return EmptyResultSet() | 2118 | return EmptyResultSet() |
723 | 2120 | distribution, query = self.parseToken(query) | ||
724 | 2121 | if distribution is None: | ||
725 | 2122 | # This could failover to ubuntu, but that is non-obvious. The | ||
726 | 2123 | # Python widget must set the default distribution and the JS | ||
727 | 2124 | # widget must encourage the <distro>/<package> search format. | ||
728 | 2125 | return EmptyResultSet() | ||
729 | 2126 | 2119 | ||
730 | 2127 | query = unicode(query) | 2120 | query = unicode(query) |
731 | 2128 | query_re = re.escape(query) | 2121 | query_re = re.escape(query) |
732 | @@ -2143,9 +2136,10 @@ | |||
733 | 2143 | ), | 2136 | ), |
734 | 2144 | Or( | 2137 | Or( |
735 | 2145 | DistributionSourcePackageCache.archiveID.is_in( | 2138 | DistributionSourcePackageCache.archiveID.is_in( |
737 | 2146 | distribution.all_distro_archive_ids), | 2139 | self.distribution.all_distro_archive_ids), |
738 | 2147 | DistributionSourcePackageCache.archive == None), | 2140 | DistributionSourcePackageCache.archive == None), |
740 | 2148 | DistributionSourcePackageCache.distribution == distribution, | 2141 | DistributionSourcePackageCache.distribution == |
741 | 2142 | self.distribution, | ||
742 | 2149 | ), | 2143 | ), |
743 | 2150 | tables=DistributionSourcePackageCache)) | 2144 | tables=DistributionSourcePackageCache)) |
744 | 2151 | SearchableDSPC = Table("SearchableDSPC") | 2145 | SearchableDSPC = Table("SearchableDSPC") |
745 | @@ -2178,7 +2172,7 @@ | |||
746 | 2178 | (DistributionSourcePackageInDatabase, | 2172 | (DistributionSourcePackageInDatabase, |
747 | 2179 | searchable_dspc_binpkgnames), | 2173 | searchable_dspc_binpkgnames), |
748 | 2180 | DistributionSourcePackageInDatabase.distribution == | 2174 | DistributionSourcePackageInDatabase.distribution == |
750 | 2181 | distribution, | 2175 | self.distribution, |
751 | 2182 | DistributionSourcePackageInDatabase.sourcepackagename_id == | 2176 | DistributionSourcePackageInDatabase.sourcepackagename_id == |
752 | 2183 | searchable_dspc_sourcepackagename) | 2177 | searchable_dspc_sourcepackagename) |
753 | 2184 | results.order_by(Desc(rank), searchable_dspc_name) | 2178 | results.order_by(Desc(rank), searchable_dspc_name) |
754 | 2185 | 2179 | ||
755 | === modified file 'lib/lp/services/webapp/configure.zcml' | |||
756 | --- lib/lp/services/webapp/configure.zcml 2014-11-28 22:07:05 +0000 | |||
757 | +++ lib/lp/services/webapp/configure.zcml 2016-07-27 17:22:45 +0000 | |||
758 | @@ -1,4 +1,4 @@ | |||
760 | 1 | <!-- Copyright 2009-2011 Canonical Ltd. This software is licensed under the | 1 | <!-- Copyright 2009-2016 Canonical Ltd. This software is licensed under the |
761 | 2 | GNU Affero General Public License version 3 (see the file LICENSE). | 2 | GNU Affero General Public License version 3 (see the file LICENSE). |
762 | 3 | --> | 3 | --> |
763 | 4 | 4 | ||
764 | @@ -413,6 +413,17 @@ | |||
765 | 413 | permission="zope.Public" | 413 | permission="zope.Public" |
766 | 414 | /> | 414 | /> |
767 | 415 | 415 | ||
768 | 416 | <!-- Define the widget used by fields that use | ||
769 | 417 | DistributionSourcePackageVocabulary. --> | ||
770 | 418 | <view | ||
771 | 419 | type="zope.publisher.interfaces.browser.IBrowserRequest" | ||
772 | 420 | for="zope.schema.interfaces.IChoice | ||
773 | 421 | lp.registry.vocabularies.DistributionSourcePackageVocabulary" | ||
774 | 422 | provides="zope.formlib.interfaces.IInputWidget" | ||
775 | 423 | factory="lp.app.widgets.popup.DistributionSourcePackagePickerWidget" | ||
776 | 424 | permission="zope.Public" | ||
777 | 425 | /> | ||
778 | 426 | |||
779 | 416 | <!-- A simple view used by the page tests. --> | 427 | <!-- A simple view used by the page tests. --> |
780 | 417 | <browser:page | 428 | <browser:page |
781 | 418 | for="lp.services.webapp.interfaces.ILaunchpadRoot" | 429 | for="lp.services.webapp.interfaces.ILaunchpadRoot" |