← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/fix-dsp-vocab-picker into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/fix-dsp-vocab-picker into lp:launchpad.

Commit message:
Customise the picker for DistributionSourcePackageVocabulary to make vocabulary requests in the context of the currently-selected distribution.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #42298 in Launchpad itself: "package picker lists unpublished (invalid) packages"
  https://bugs.launchpad.net/launchpad/+bug/42298

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/fix-dsp-vocab-picker/+merge/300782

Customise the picker for DistributionSourcePackageVocabulary to make vocabulary requests in the context of the currently-selected distribution.  Otherwise the vocabulary only works if explicitly given the <distro>/<package> search format, which isn't very user-friendly.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/fix-dsp-vocab-picker into lp:launchpad.
=== modified file 'lib/lp/app/javascript/picker/picker_patcher.js'
--- lib/lp/app/javascript/picker/picker_patcher.js	2012-09-07 14:42:48 +0000
+++ lib/lp/app/javascript/picker/picker_patcher.js	2016-07-21 15:25:29 +0000
@@ -1,4 +1,4 @@
-/* Copyright 2011 Canonical Ltd.  This software is licensed under the
+/* Copyright 2011-2016 Canonical Ltd.  This software is licensed under the
  * GNU Affero General Public License version 3 (see the file LICENSE).
  */
 
@@ -620,7 +620,9 @@
             // use the context to limit the results to the same project.
 
             var uri = '';
-            if (Y.Lang.isValue(config.context)){
+            if (Y.Lang.isFunction(config.getContextPath)) {
+                uri += config.getContextPath() + '/';
+            } else if (Y.Lang.isValue(config.context)) {
                 uri += Y.lp.get_url_path(
                     config.context.get('web_link')) + '/';
             }

=== modified file 'lib/lp/app/javascript/picker/tests/test_picker_patcher.js'
--- lib/lp/app/javascript/picker/tests/test_picker_patcher.js	2013-03-20 03:41:40 +0000
+++ lib/lp/app/javascript/picker/tests/test_picker_patcher.js	2016-07-21 15:25:29 +0000
@@ -1,4 +1,4 @@
-/* Copyright 2012 Canonical Ltd.  This software is licensed under the
+/* Copyright 2012-2016 Canonical Ltd.  This software is licensed under the
  * GNU Affero General Public License version 3 (see the file LICENSE). */
 
 YUI.add('lp.app.picker.test', function (Y) {
@@ -33,7 +33,7 @@
     }
 
     tests.suite.add(new Y.Test.Case({
-        name: 'picker_yesyno_validation',
+        name: 'picker_yesno_validation',
 
         setUp: function() {
             this.vocabulary = [
@@ -514,6 +514,39 @@
 
     tests.suite.add(new Y.Test.Case({
 
+        name: 'picker_custom_context_path',
+
+        create_picker: function(yio, extra_config) {
+            var config = {yio: yio};
+            if (extra_config !== undefined) {
+                config = Y.merge(extra_config, config);
+            }
+            return Y.lp.app.picker.addPickerPatcher(
+                "Foo",
+                "foo/bar",
+                "test_link",
+                "picker_id",
+                config);
+        },
+
+        test_custom_context_path_honoured: function() {
+            var mock_io = new Y.lp.testing.mockio.MockIo();
+            var extra_config = {
+                "getContextPath": function() { return "/gentoo"; }
+            };
+            var picker = this.create_picker(mock_io, extra_config);
+            picker.fire('search', 'package');
+            Assert.areEqual(1, mock_io.requests.length);
+            Assert.areEqual(
+                '/gentoo/@@+huge-vocabulary?name=Foo&search_text=package&' +
+                'batch=6&start=0', mock_io.requests[0].url);
+            cleanup_widget(picker);
+        }
+
+    }));
+
+    tests.suite.add(new Y.Test.Case({
+
         name: 'picker_error_handling',
 
         setUp: function() {

=== modified file 'lib/lp/app/widgets/launchpadtarget.py'
--- lib/lp/app/widgets/launchpadtarget.py	2016-06-10 22:06:13 +0000
+++ lib/lp/app/widgets/launchpadtarget.py	2016-07-21 15:25:29 +0000
@@ -29,6 +29,7 @@
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.app.validators import LaunchpadValidationError
 from lp.app.widgets.itemswidgets import LaunchpadDropdownWidget
+from lp.app.widgets.popup import VocabularyPickerWidget
 from lp.registry.interfaces.distribution import IDistribution
 from lp.registry.interfaces.distributionsourcepackage import (
     IDistributionSourcePackage,
@@ -194,3 +195,13 @@
         self.setUpSubWidgets()
         self.setUpOptions()
         return self.template()
+
+
+class LaunchpadTargetPopupWidget(VocabularyPickerWidget):
+    """Custom popup widget for choosing distribution/package combinations."""
+
+    __call__ = ViewPageTemplateFile('templates/launchpad-target-picker.pt')
+
+    @property
+    def distribution_id(self):
+        return self._prefix + "distribution"

=== modified file 'lib/lp/app/widgets/templates/form-picker-macros.pt'
--- lib/lp/app/widgets/templates/form-picker-macros.pt	2012-02-01 23:47:54 +0000
+++ lib/lp/app/widgets/templates/form-picker-macros.pt	2016-07-21 15:25:29 +0000
@@ -29,7 +29,7 @@
     </tal:search_results>
 
     <tal:chooseLink replace="structure view/chooseLink" />
-    <script tal:content="structure string:
+    <script metal:define-slot="add-picker" tal:content="structure string:
     LPJS.use('node', 'lp.app.picker', function(Y) {
         var config = ${view/json_config};
         var show_widget_id = '${view/show_widget_id}';

=== added file 'lib/lp/app/widgets/templates/launchpad-target-picker.pt'
--- lib/lp/app/widgets/templates/launchpad-target-picker.pt	1970-01-01 00:00:00 +0000
+++ lib/lp/app/widgets/templates/launchpad-target-picker.pt	2016-07-21 15:25:29 +0000
@@ -0,0 +1,23 @@
+<tal:root
+    xmlns:tal="http://xml.zope.org/namespaces/tal";
+    xmlns:metal="http://xml.zope.org/namespaces/metal";
+    omit-tag="">
+
+<metal:form-picker use-macro="context/@@form-picker-macros/form-picker">
+  <script metal:fill-slot="add-picker" tal:content="structure string:
+  LPJS.use('node', 'lp.app.picker', function(Y) {
+      var config = ${view/json_config};
+      if ('${view/distribution_id|nothing}' !== '') {
+          config.getContextPath = function() {
+              return '/' + Y.DOM.byId('${view/distribution_id|nothing}').value;
+          };
+      }
+      var show_widget_id = '${view/show_widget_id}';
+      Y.on('domready', function(e) {
+          Y.lp.app.picker.addPicker(config, show_widget_id);
+      });
+  });
+  "/>
+</metal:form-picker>
+
+</tal:root>

=== modified file 'lib/lp/app/widgets/tests/test_launchpadtarget.py'
--- lib/lp/app/widgets/tests/test_launchpadtarget.py	2016-07-11 22:35:59 +0000
+++ lib/lp/app/widgets/tests/test_launchpadtarget.py	2016-07-21 15:25:29 +0000
@@ -6,7 +6,10 @@
 import re
 
 from BeautifulSoup import BeautifulSoup
-from lazr.restful.fields import Reference
+from lazr.restful.fields import (
+    Reference,
+    ReferenceChoice,
+    )
 from zope.formlib.interfaces import (
     IBrowserWidget,
     IInputWidget,
@@ -16,9 +19,13 @@
     implementer,
     Interface,
     )
+from zope.schema.vocabulary import getVocabularyRegistry
 
 from lp.app.validators import LaunchpadValidationError
-from lp.app.widgets.launchpadtarget import LaunchpadTargetWidget
+from lp.app.widgets.launchpadtarget import (
+    LaunchpadTargetPopupWidget,
+    LaunchpadTargetWidget,
+    )
 from lp.registry.vocabularies import (
     DistributionSourcePackageVocabulary,
     DistributionVocabulary,
@@ -82,7 +89,7 @@
         # The render template is setup.
         self.assertTrue(
             self.widget.template.filename.endswith('launchpad-target.pt'),
-            'Template was not setup.')
+            'Template was not set up.')
 
     def test_default_option(self):
         # This package field is the default option.
@@ -329,3 +336,33 @@
         fields = soup.findAll(['input', 'select'], {'id': re.compile('.*')})
         ids = [field['id'] for field in fields]
         self.assertContentEqual(expected_ids, ids)
+
+
+class LaunchpadTargetPopupWidgetTestCase(TestCaseWithFactory):
+    """Test the LaunchpadTargetPopupWidget class."""
+
+    layer = DatabaseFunctionalLayer
+
+    def setUp(self):
+        super(LaunchpadTargetPopupWidgetTestCase, self).setUp()
+        context = Thing()
+        field = ReferenceChoice(
+            __name__='target', schema=Interface, title=u'target',
+            vocabulary='DistributionSourcePackage')
+        field = field.bind(context)
+        vocabulary = getVocabularyRegistry().get(
+            context, 'DistributionSourcePackage')
+        request = LaunchpadTestRequest()
+        self.widget = LaunchpadTargetPopupWidget(field, vocabulary, request)
+        self.widget.setPrefix('field.target')
+
+    def test_distribution_id(self):
+        self.assertEqual(
+            'field.target.distribution', self.widget.distribution_id)
+
+    def test_call(self):
+        markup = self.widget()
+        # It's difficult to do a particularly accurate test here, but we can
+        # at least check that code to get the value of the distribution
+        # field is present.
+        self.assertIn("Y.DOM.byId('field.target.distribution').value", markup)

=== modified file 'lib/lp/services/webapp/configure.zcml'
--- lib/lp/services/webapp/configure.zcml	2014-11-28 22:07:05 +0000
+++ lib/lp/services/webapp/configure.zcml	2016-07-21 15:25:29 +0000
@@ -1,4 +1,4 @@
-<!-- Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
+<!-- Copyright 2009-2016 Canonical Ltd.  This software is licensed under the
      GNU Affero General Public License version 3 (see the file LICENSE).
 -->
 
@@ -413,6 +413,17 @@
       permission="zope.Public"
       />
 
+    <!-- Define the widget used by fields that use
+         DistributionSourcePackageVocabulary. -->
+    <view
+      type="zope.publisher.interfaces.browser.IBrowserRequest"
+      for="zope.schema.interfaces.IChoice
+        lp.registry.vocabularies.DistributionSourcePackageVocabulary"
+      provides="zope.formlib.interfaces.IInputWidget"
+      factory="lp.app.widgets.launchpadtarget.LaunchpadTargetPopupWidget"
+      permission="zope.Public"
+      />
+
     <!-- A simple view used by the page tests. -->
     <browser:page
         for="lp.services.webapp.interfaces.ILaunchpadRoot"


Follow ups