← Back to team overview

launchpad-reviewers team mailing list archive

lp:~edwin-grubbs/launchpad/bug-602385-register-project-from-sourcepackage-page-part2 into lp:launchpad

 

Edwin Grubbs has proposed merging lp:~edwin-grubbs/launchpad/bug-602385-register-project-from-sourcepackage-page-part2 into lp:launchpad with lp:~edwin-grubbs/launchpad/bug-602385-register-project-from-sourcepackage-page as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  #602385 Allow SPs to register an upstream project
  https://bugs.launchpad.net/bugs/602385


Summary
-------

This branch finishes the work on bug 602385. When you register a project
off of the source package page, you will now get to see the source
package's copyright info, so that you can choose the license more
easily. When the project is created, it will automatically be linked to
the source package.


Implementation details
----------------------

Added LicenseWidget.source_package_release attribute, so that the widget
template can display $source_package_release/+copyright.
    lib/canonical/widgets/product.py
    lib/canonical/widgets/templates/license.pt

Add source_package_name and distroseries hidden fields to
ProjectAddStepOne/Two. Removed unnecessary copying of data variables
into self.request.form, since they are already available as
self.request.form['field.*'].
Verify that the user is redirected back to the source package, that they
are linked, and that there is an informational message explaining that.
    lib/lp/registry/browser/product.py
    lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt
    lib/lp/registry/templates/product-new.pt

Add source_package_name and distroseries to the url that takes you from
the source package page to the /projects/+new page. Since the source
package is autolinked after creating the project, there is no need to
redirect the user back to +edit-packaging, so the return_url is now just
set to the canonical url for the source package.
    lib/lp/registry/browser/sourcepackage.py
    lib/lp/registry/browser/tests/test_sourcepackage_views.py

Fixed bad tests that were dependent on copying fields from data to
self.request.form.
    lib/lp/registry/browser/tests/project-add-views.txt

Tests
-----

./bin/test -vv -t 'test_sourcepackage_views|project-add-views|xx-sourcepackage-packaging'

Demo and Q/A
------------

Add copyright information on launchpad.dev by running this query.
 UPDATE SourcePackageRelease
 SET copyright =
   'The following line should be highlighted since it looks like a license
    /usr/share/common-licenses/GPL-2
    foo
    bar
    baz
    1
    2
    3
    4';

* Open http://launchpad.dev/ubuntu/hoary/+source/pmount
    1. Select "Register the upstream project" radio button.
    2. Click on "Link to Upstream Project"
    3. You should now be on Step 2 of the /projects/+new page.
    4. Verify that clicking on the green "Copyright info from source package"
       link expands the copyright.
    5. Select a license.
    6. Click on "Complete registration and link to pmount package".
    7. You now be redirected back to
       http://launchpad.dev/ubuntu/hoary/+source/pmount
       There should be an informational message saying:
       "Linked Pmount project to pmount source package"
       and the "Upstream connections" portlet should show Pmount=>trunk.
* Click on the yellow edit-icon next to Pmount=>trunk.
    * Click on the "Register the upstream project" link.
    * The /projects/+new page should give you an error that pmount is
      already used by another project.
    * Choose a different URL, click Continue, click
      "No, this is a new project", and then follow steps 3 through 6 above.
    * If you didn't use a different displayname, you will have to hover
      over the first link in Pmount=>trunk to see that it points to the
      new pmount project and not the first one you created.
-- 
https://code.launchpad.net/~edwin-grubbs/launchpad/bug-602385-register-project-from-sourcepackage-page-part2/+merge/32234
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~edwin-grubbs/launchpad/bug-602385-register-project-from-sourcepackage-page-part2 into lp:launchpad.
=== modified file 'lib/canonical/widgets/product.py'
--- lib/canonical/widgets/product.py	2010-06-21 04:08:54 +0000
+++ lib/canonical/widgets/product.py	2010-08-10 18:09:36 +0000
@@ -316,6 +316,7 @@
             self, 'license_info', self.license_info, IInputWidget,
             prefix='field', value=initial_value,
             context=field.context)
+        self.source_package_release = None
         # These will get filled in by _categorize().  They are the number of
         # selected licenses in the category.  The actual count doesn't matter,
         # since if it's greater than 0 it will start opened.  NOte that we

=== modified file 'lib/canonical/widgets/templates/license.pt'
--- lib/canonical/widgets/templates/license.pt	2009-07-17 17:59:07 +0000
+++ lib/canonical/widgets/templates/license.pt	2010-08-10 18:09:36 +0000
@@ -76,6 +76,7 @@
                 target.slide.run();
             }, target_name);
         }
+        make_slider({which: 'copyright'});
         make_slider({which: 'recommended'});
         make_slider({which: 'more'});
         make_slider({which: 'deprecated'});
@@ -136,6 +137,23 @@
 //]]>
 </script>
 <div style="color: black">
+  <tal:copyright condition="view/source_package_release">
+    <a href="" id="copyright-expand" class="js-action">
+      <img id="copyright-expand-arrow"
+            src="/@@/treeCollapsed"
+            title="Copyright info from source package"
+            alt="Copyright info from source package"
+            tal:attributes="count view/more_count"/>
+      Copyright info from source package
+    </a>
+    <div id="copyright">
+      <div
+        tal:content="structure view/source_package_release/@@+copyright"
+        style="overflow-x: hidden; overflow-y: auto;
+               max-width: 60em; max-height: 32em; background: #f7f7f7"
+        />
+    </div>
+  </tal:copyright>
 
   Select the license(s) under which you release your project.
   <div tal:condition="view/allow_pending_license"

=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py	2010-08-10 18:09:15 +0000
+++ lib/lp/registry/browser/product.py	2010-08-10 18:09:36 +0000
@@ -83,6 +83,7 @@
 from lp.registry.interfaces.pillar import IPillarNameSet
 from lp.registry.interfaces.product import IProductReviewSearch, License
 from lp.registry.interfaces.series import SeriesStatus
+from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
 from lp.registry.interfaces.product import (
     IProduct, IProductSet, LicenseStatus)
 from lp.registry.interfaces.productrelease import (
@@ -1833,6 +1834,17 @@
         return canonical_url(self.product)
 
 
+def create_source_package_fields():
+    return form.Fields(
+        Choice(__name__='source_package_name',
+               vocabulary='SourcePackageName',
+               required=False),
+        Choice(__name__='distroseries',
+               vocabulary='DistroSeries',
+               required=False),
+        )
+
+
 class ProjectAddStepOne(StepView):
     """product/+new view class for creating a new project."""
 
@@ -1849,6 +1861,19 @@
     step_description = 'Project basics'
     search_results_count = 0
 
+    def setUpFields(self):
+        """See `LaunchpadFormView`."""
+        super(ProjectAddStepOne, self).setUpFields()
+        self.form_fields = (
+            self.form_fields +
+            create_source_package_fields())
+
+    def setUpWidgets(self):
+        """See `LaunchpadFormView`."""
+        super(ProjectAddStepOne, self).setUpWidgets()
+        self.widgets['source_package_name'].visible = False
+        self.widgets['distroseries'].visible = False
+
     @property
     def _return_url(self):
         """This view is using the hidden _return_url field.
@@ -1872,9 +1897,6 @@
     def main_action(self, data):
         """See `MultiStepView`."""
         self.next_step = self._next_step
-        self.request.form['displayname'] = data['displayname']
-        self.request.form['name'] = data['name'].lower()
-        self.request.form['summary'] = data['summary']
 
     # Make this a safe_action, so that the sourcepackage page can skip
     # the first step with a link (GET request) providing form values.
@@ -1887,7 +1909,6 @@
     _field_names = ['displayname', 'name', 'title', 'summary',
                     'description', 'licenses', 'license_info',
                     ]
-    main_action_label = u'Complete Registration'
     schema = IProduct
     step_name = 'projectaddstep2'
     template = ViewPageTemplateFile('../templates/product-new.pt')
@@ -1901,6 +1922,15 @@
     custom_widget('license_info', GhostWidget)
 
     @property
+    def main_action_label(self):
+        if self.source_package_name is None:
+            return u'Complete Registration'
+        else:
+            return u'Complete registration and link to %s package' % (
+                self.source_package_name.name,
+                )
+
+    @property
     def _return_url(self):
         """This view is using the hidden _return_url field.
 
@@ -1921,7 +1951,8 @@
         """See `LaunchpadFormView`."""
         super(ProjectAddStepTwo, self).setUpFields()
         self.form_fields = (self.form_fields +
-                            self._createDisclaimMaintainerField())
+                            self._createDisclaimMaintainerField() +
+                            create_source_package_fields())
 
     def _createDisclaimMaintainerField(self):
         """Return a Bool field for disclaiming maintainer.
@@ -1954,12 +1985,39 @@
                                      "this will be the project's URL.")
         self.widgets['displayname'].visible = False
 
+        self.widgets['source_package_name'].visible = False
+        self.widgets['distroseries'].visible = False
+
+        # Set the source_package_release attribute on the licenses
+        # widget, so that the source package's copyright info can be
+        # displayed.
+        ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
+        if self.source_package_name is not None:
+            release_list = ubuntu.getCurrentSourceReleases(
+                [self.source_package_name])
+            if len(release_list) != 0:
+                self.widgets['licenses'].source_package_release = (
+                    release_list.items()[0][1])
+
+    @property
+    def source_package_name(self):
+        # setUpWidgets() doesn't have access to the data dictionary,
+        # so the source package name needs to be converted from a string
+        # into an object here.
+        package_name_string = self.request.form.get(
+            'field.source_package_name')
+        if package_name_string is None:
+            return None
+        else:
+            return getUtility(ISourcePackageNameSet).queryByName(
+                package_name_string)
+
     @cachedproperty
     def _search_string(self):
         """Return the ORed terms to match."""
-        search_text = SPACE.join((self.request.form['name'],
-                                  self.request.form['displayname'],
-                                  self.request.form['summary']))
+        search_text = SPACE.join((self.request.form['field.name'],
+                                  self.request.form['field.displayname'],
+                                  self.request.form['field.summary']))
         # OR all the terms together.
         return OR.join(search_text.split())
 
@@ -1996,7 +2054,8 @@
     def label(self):
         """See `LaunchpadFormView`."""
         return 'Register %s (%s) in Launchpad' % (
-                self.request.form['displayname'], self.request.form['name'])
+                self.request.form['field.displayname'],
+                self.request.form['field.name'])
 
     def create_product(self, data):
         """Create the product from the user data."""
@@ -2020,11 +2079,24 @@
             license_info=data['license_info'],
             project=project)
 
+    def link_source_package(self, data):
+        if (data.get('distroseries') is not None
+            and self.source_package_name is not None):
+            source_package = data['distroseries'].getSourcePackage(
+                self.source_package_name)
+            source_package.setPackaging(
+                self.product.development_focus, self.user)
+            self.request.response.addInfoNotification(
+                'Linked %s project to %s source package.' % (
+                    self.product.displayname, self.source_package_name.name))
+
     def main_action(self, data):
         """See `MultiStepView`."""
         self.product = self.create_product(data)
         self.notifyCommercialMailingList()
         notify(ObjectCreatedEvent(self.product))
+        self.link_source_package(data)
+
         if self._return_url is None:
             self.next_url = canonical_url(self.product)
         else:

=== modified file 'lib/lp/registry/browser/sourcepackage.py'
--- lib/lp/registry/browser/sourcepackage.py	2010-08-10 18:09:15 +0000
+++ lib/lp/registry/browser/sourcepackage.py	2010-08-10 18:09:36 +0000
@@ -66,10 +66,15 @@
 from canonical.lazr.utils import smartquote
 
 
-def get_register_upstream_url(source_package, return_url):
+def get_register_upstream_url(source_package):
     displayname = string.capwords(source_package.name.replace('-', ' '))
+    distroseries_string = "%s/%s" % (
+        source_package.distroseries.distribution.name,
+        source_package.distroseries.name)
     params = {
-        '_return_url': return_url,
+        '_return_url': canonical_url(source_package),
+        'field.source_package_name': source_package.sourcepackagename.name,
+        'field.distroseries': distroseries_string,
         'field.name': source_package.name,
         'field.displayname': displayname,
         'field.title': displayname,
@@ -220,8 +225,7 @@
 
     @property
     def register_upstream_url(self):
-        return get_register_upstream_url(
-            self.context, return_url=self.request.getURL())
+        return get_register_upstream_url(self.context)
 
 
 class SourcePackageChangeUpstreamStepTwo(ReturnToReferrerMixin, StepView):
@@ -526,8 +530,7 @@
             return
         elif upstream is self.register_upstream:
             # The user wants to create a new project.
-            url = get_register_upstream_url(
-                self.context, return_url=self.request.getURL())
+            url = get_register_upstream_url(self.context)
             self.request.response.redirect(url)
             return
         self.context.setPackaging(upstream.development_focus, self.user)

=== modified file 'lib/lp/registry/browser/tests/project-add-views.txt'
--- lib/lp/registry/browser/tests/project-add-views.txt	2010-05-25 04:41:12 +0000
+++ lib/lp/registry/browser/tests/project-add-views.txt	2010-08-10 18:09:36 +0000
@@ -15,25 +15,24 @@
 are forwarded in the form data to the second step.  The title is also
 forwarded, but is only required by the Zope machinery, not the view.
 
-    >>> form = {'field.actions.continue': 'Continue'}
+    >>> from lp.registry.browser.product import ProjectAddStepOne
+    >>> form = {
+    ...     'field.actions.continue': 'Continue',
+    ...     'field.__visited_steps__': ProjectAddStepOne.step_name,
+    ...     'field.displayname': '',
+    ...     'field.name': '',
+    ...     'field.summary': '',
+    ... }
 
     >>> view = create_initialized_view(product_set, name='+new', form=form)
-    Traceback (most recent call last):
-    ...
-    KeyError: 'displayname'
+    >>> for error in view.view.errors:
+    ...     print error
+    ('displayname', 'Name', RequiredMissing())
+    ('name', 'URL', RequiredMissing())
+    ('summary', u'Summary', RequiredMissing())
 
     >>> form['field.displayname'] = 'Snowdog'
-    >>> view = create_initialized_view(product_set, name='+new', form=form)
-    Traceback (most recent call last):
-    ...
-    KeyError: 'name'
-
     >>> form['field.name'] = 'snowdog'
-    >>> view = create_initialized_view(product_set, name='+new', form=form)
-    Traceback (most recent call last):
-    ...
-    KeyError: 'summary'
-
     >>> form['field.summary'] = 'By-tor and the Snowdog'
     >>> view = create_initialized_view(product_set, name='+new', form=form)
 
@@ -44,7 +43,6 @@
     # steps individually.
 
     >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
-    >>> from lp.registry.browser.product import ProjectAddStepOne
 
     >>> form['field.__visited_steps__'] = ProjectAddStepOne.step_name
     >>> request = LaunchpadTestRequest(form=form, method='POST')
@@ -63,10 +61,12 @@
 
     >>> from lp.registry.browser.product import ProjectAddStepTwo
     >>> form = {
-    ...     'displayname': 'Snowdog',
-    ...     'name': 'snowdog',
-    ...     'title': 'The Snowdog',
-    ...     'summary': 'By-tor and the Snowdog',
+    ...     'field.actions.continue': 'Continue',
+    ...     'field.__visited_steps__': ProjectAddStepTwo.step_name,
+    ...     'field.displayname': 'Snowdog',
+    ...     'field.name': 'snowdog',
+    ...     'field.title': 'The Snowdog',
+    ...     'field.summary': 'By-tor and the Snowdog',
     ...     }
 
     >>> request = LaunchpadTestRequest(form=form, method='POST')
@@ -90,7 +90,7 @@
 existing projects for possible matches.  By tweaking the project summary, we
 can see that there are search results available.
 
-    >>> form['summary'] = 'My Snowdog ate your Firefox'
+    >>> form['field.summary'] = 'My Snowdog ate your Firefox'
 
     >>> request = LaunchpadTestRequest(form=form, method='POST')
     >>> view = ProjectAddStepTwo(product_set, request)
@@ -229,8 +229,8 @@
     questions.
     <BLANKLINE>
     Sometimes new projects are licensed as 'Other/Open Source' because the
-    licensing decisions have not yet been made.  If that is your situation we u=
-    rge
+    licensing decisions have not yet been made.
+    If that is your situation we u= rge
     you to update the licensing in Launchpad as soon as you make that choice.
     <BLANKLINE>
     If the license for your project needs to be corrected you can do so by

=== modified file 'lib/lp/registry/browser/tests/test_sourcepackage_views.py'
--- lib/lp/registry/browser/tests/test_sourcepackage_views.py	2010-08-10 18:09:15 +0000
+++ lib/lp/registry/browser/tests/test_sourcepackage_views.py	2010-08-10 18:09:36 +0000
@@ -9,11 +9,16 @@
 import urllib
 
 from zope.component import getUtility
+from zope.interface import implements
 
 from canonical.testing import DatabaseFunctionalLayer
 
+
 from lp.registry.browser.sourcepackage import get_register_upstream_url
-from lp.registry.interfaces.distroseries import IDistroSeriesSet
+from lp.registry.interfaces.distribution import IDistribution
+from lp.registry.interfaces.distroseries import (
+    IDistroSeries, IDistroSeriesSet)
+from lp.registry.interfaces.sourcepackage import ISourcePackage
 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
 from lp.testing import TestCaseWithFactory
 
@@ -24,25 +29,35 @@
     layer = DatabaseFunctionalLayer
 
     def test_get_register_upstream_url_displayname(self):
+        distroseries = self.factory.makeDistroRelease(
+            distribution=self.factory.makeDistribution(name='zoobuntu'),
+            name='walrus')
         source_package = self.factory.makeSourcePackage(
+            distroseries=distroseries,
             sourcepackagename='python-super-package')
-        return_url = 'http://example.com/foo?a=b&c=d'
-        url = get_register_upstream_url(source_package, return_url)
-        expected_url = (
-            '/projects/+new?'
-            '_return_url='
-            'http%3A%2F%2Fexample.com%2Ffoo%3Fa%3Db%26c%3Dd'
-            '&field.__visited_steps__=projectaddstep1'
-            '&field.actions.continue=Continue'
+        url = get_register_upstream_url(source_package)
+        expected_base = '/projects/+new'
+        expected_params = [
+            ('_return_url',
+             'http://launchpad.dev/zoobuntu/walrus/'
+             '+source/python-super-package'),
+            ('field.__visited_steps__', 'projectaddstep1'),
+            ('field.actions.continue', 'Continue'),
             # The sourcepackagename 'python-super-package' is split on
             # the hyphens, and each word is capitalized.
-            '&field.displayname=Python+Super+Package'
-            '&field.name=python-super-package'
-            # The summary is empty, since the source package doesn't
-            # have a binary package release.
-            '&field.summary='
-            '&field.title=Python+Super+Package')
-        self.assertEqual(expected_url, url)
+            ('field.displayname', 'Python Super Package'),
+            ('field.distroseries', 'zoobuntu/walrus'),
+            ('field.name', 'python-super-package'),
+            # The summary is missing, since the source package doesn't
+            # have a binary package release, and parse_qsl() excludes
+            # empty params.
+            ('field.source_package_name', 'python-super-package'),
+            ('field.title', 'Python Super Package'),
+            ]
+        base, query = urllib.splitquery(url)
+        params = cgi.parse_qsl(query)
+        self.assertEqual((expected_base, expected_params),
+                         (base, params))
 
     def test_get_register_upstream_url_summary(self):
         test_publisher = SoyuzTestPublisher()
@@ -57,15 +72,17 @@
         # objects need to be reloaded.
         distroseries = getUtility(IDistroSeriesSet).get(distroseries_id)
         source_package = distroseries.getSourcePackage(source_package_name)
-        return_url = 'http://example.com/foo?a=b&c=d'
-        url = get_register_upstream_url(source_package, return_url)
+        url = get_register_upstream_url(source_package)
         expected_base = '/projects/+new'
         expected_params = [
-            ('_return_url', 'http://example.com/foo?a=b&c=d'),
+            ('_return_url',
+             'http://launchpad.dev/youbuntu/busy/+source/bonkers'),
             ('field.__visited_steps__', 'projectaddstep1'),
             ('field.actions.continue', 'Continue'),
             ('field.displayname', 'Bonkers'),
+            ('field.distroseries', 'youbuntu/busy'),
             ('field.name', 'bonkers'),
+            ('field.source_package_name', 'bonkers'),
             ('field.summary', 'summary for flubber-bin\n'
                               + 'summary for flubber-lib'),
             ('field.title', 'Bonkers'),
@@ -77,32 +94,47 @@
 
     def test_get_register_upstream_url_summary_duplicates(self):
 
-        class FakeDistroSeriesBinaryPackage:
-            def __init__(self, summary):
-                self.summary = summary
-
-        class FakeDistributionSourcePackageRelease:
-            sample_binary_packages = [
-                FakeDistroSeriesBinaryPackage('summary for foo'),
-                FakeDistroSeriesBinaryPackage('summary for bar'),
-                FakeDistroSeriesBinaryPackage('summary for baz'),
-                FakeDistroSeriesBinaryPackage('summary for baz'),
-                ]
-
-        class FakeSourcePackage:
-            name = 'foo'
-            releases = [FakeDistributionSourcePackageRelease()]
-
-        source_package = FakeSourcePackage()
-        return_url = 'http://example.com/foo?a=b&c=d'
-        url = get_register_upstream_url(source_package, return_url)
+        class Faker:
+            # Fakes attributes easily.
+            def __init__(self, **kw):
+                self.__dict__.update(kw)
+
+        class FakeSourcePackage(Faker):
+            # Interface necessary for canonical_url() call in
+            # get_register_upstream_url().
+            implements(ISourcePackage)
+
+        class FakeDistroSeries(Faker):
+            implements(IDistroSeries)
+
+        class FakeDistribution(Faker):
+            implements(IDistribution)
+
+        releases = Faker(sample_binary_packages=[
+            Faker(summary='summary for foo'),
+            Faker(summary='summary for bar'),
+            Faker(summary='summary for baz'),
+            Faker(summary='summary for baz'),
+            ])
+        source_package = FakeSourcePackage(
+            name='foo',
+            sourcepackagename=Faker(name='foo'),
+            distroseries=FakeDistroSeries(
+                name='walrus',
+                distribution=FakeDistribution(name='zoobuntu')),
+            releases=[releases])
+
+        url = get_register_upstream_url(source_package)
         expected_base = '/projects/+new'
         expected_params = [
-            ('_return_url', 'http://example.com/foo?a=b&c=d'),
+            ('_return_url',
+             'http://launchpad.dev/zoobuntu/walrus/+source/foo'),
             ('field.__visited_steps__', 'projectaddstep1'),
             ('field.actions.continue', 'Continue'),
             ('field.displayname', 'Foo'),
+            ('field.distroseries', 'zoobuntu/walrus'),
             ('field.name', 'foo'),
+            ('field.source_package_name', 'foo'),
             ('field.summary', 'summary for bar\n'
                               + 'summary for baz\n'
                               + 'summary for foo'),

=== modified file 'lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt'
--- lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt	2010-08-10 18:09:15 +0000
+++ lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt	2010-08-10 18:09:36 +0000
@@ -84,11 +84,13 @@
     ...     'Register the upstream project').selected = True
     >>> user_browser.getControl("Link to Upstream Project").click()
     >>> print user_browser.url.replace('&', '\n&')
-    http://launchpad.dev/projects/+new?_return_url=http...%2Bindex
+    http://launchpad.dev/projects/+new?_return_url=http...%2Bsource%2Fbonkers
     &field.__visited_steps__=projectaddstep1
     &field.actions.continue=Continue
     &field.displayname=Bonkers
+    &field.distroseries=youbuntu%2Fbusy
     &field.name=bonkers
+    &field.source_package_name=bonkers
     &field.summary=summary+for+flubber-bin%0Asummary+for+flubber-lib
     &field.title=Bonkers
     >>> print user_browser.getControl(name='field.name').value
@@ -118,11 +120,13 @@
 
     >>> user_browser.getLink("Register the upstream project").click()
     >>> print user_browser.url.replace('&', '\n&')
-    http://launchpad.dev/projects/+new?_return_url=http...%2Bedit-packaging
+    http://launchpad.dev/projects/+new?_return_url=http...%2Bsource%2Fbonkers
     &field.__visited_steps__=projectaddstep1
     &field.actions.continue=Continue
     &field.displayname=Bonkers
+    &field.distroseries=youbuntu%2Fbusy
     &field.name=bonkers
+    &field.source_package_name=bonkers
     &field.summary=summary+for+flubber-bin%0Asummary+for+flubber-lib
     &field.title=Bonkers
     >>> print user_browser.getControl(name='field.name').value
@@ -140,9 +144,19 @@
 
 If there are no problems with the prefilled data, then the license
 just needs to be selected. The user will then be redirected back
-to the source package page so that it can be linked.
+to the source package page and an informational message will be displayed.
 
     >>> user_browser.getControl(name='field.licenses').value = ['BSD']
-    >>> user_browser.getControl("Complete Registration").click()
+    >>> user_browser.getControl(
+    ...     "Complete registration and link to bonkers package").click()
     >>> print user_browser.url
-    http://launchpad.dev/youbuntu/busy/+source/bonkers/+edit-packaging
+    http://launchpad.dev/youbuntu/busy/+source/bonkers
+    >>> for tag in find_tags_by_class(
+    ...     user_browser.contents, 'informational message'):
+    ...     print extract_text(tag)
+    Linked Bonkers project to bonkers source package.
+    >>> print extract_text(
+    ...     find_tag_by_id(user_browser.contents, 'upstreams'))
+    Bonkers &rArr; trunk
+    Change upstream link
+    Remove upstream link...

=== modified file 'lib/lp/registry/templates/product-new.pt'
--- lib/lp/registry/templates/product-new.pt	2010-05-12 19:06:17 +0000
+++ lib/lp/registry/templates/product-new.pt	2010-08-10 18:09:36 +0000
@@ -299,8 +299,8 @@
           <img src="/@@/info" />
           There are similar projects already registered in Launchpad.
           Is project
-          <strong><tal:displayname tal:replace="view/request/displayname" />
-          (<tal:name  tal:replace="view/request/name" />)</strong>
+          <strong><tal:displayname tal:replace="view/request/field.displayname" />
+          (<tal:name  tal:replace="view/request/field.name" />)</strong>
           one of these?
           </div>
 
@@ -326,8 +326,8 @@
               tal:condition="view/search_results_count"
               >Registration details</h3>
           Select the licenses for project
-          <strong><tal:displayname tal:replace="view/request/displayname" />
-          (<tal:name  tal:replace="view/request/name" />)</strong>
+          <strong><tal:displayname tal:replace="view/request/field.displayname" />
+          (<tal:name  tal:replace="view/request/field.name" />)</strong>
           and complete the registration.  You may also update the project's
           title and summary.
         </div>