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

Proposed by Edwin Grubbs
Status: Merged
Approved by: Curtis Hovey
Approved revision: no longer in the source branch.
Merged at revision: 9653
Proposed branch: lp:~edwin-grubbs/launchpad/bug-602385-register-project-from-sourcepackage-page-part2
Merge into: lp:launchpad/db-devel
Prerequisite: lp:~edwin-grubbs/launchpad/bug-602385-register-project-from-sourcepackage-page
Diff against target: 772 lines (+340/-97)
10 files modified
README (+106/-4)
lib/canonical/launchpad/emailtemplates/product-other-license.txt (+3/-2)
lib/canonical/widgets/product.py (+1/-0)
lib/canonical/widgets/templates/license.pt (+23/-5)
lib/lp/registry/browser/product.py (+81/-9)
lib/lp/registry/browser/sourcepackage.py (+9/-6)
lib/lp/registry/browser/tests/project-add-views.txt (+23/-23)
lib/lp/registry/browser/tests/test_sourcepackage_views.py (+71/-39)
lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt (+19/-5)
lib/lp/registry/templates/product-new.pt (+4/-4)
To merge this branch: bzr merge lp:~edwin-grubbs/launchpad/bug-602385-register-project-from-sourcepackage-page-part2
Reviewer Review Type Date Requested Status
Curtis Hovey (community) ui Approve
Leonard Richardson (community) Approve
Review via email: mp+32234@code.launchpad.net

Description of the change

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.

To post a comment you must log in.
Revision history for this message
Leonard Richardson (leonardr) wrote :

r=me with the minor changes discussed on IRC (Seeing whether 'count' is really neccessary, reformatting the line wrap when printing out the email so as to avoid confusing readers of the tests.)

review: Approve
Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

Thanks for the review. The count was actually used by the javascript to decide whether to expand a section of licenses by the count of licenses checked. I changed the attribute to start_expanded to clearly describe its intent.

Revision history for this message
Curtis Hovey (sinzui) wrote :

This looks nice. The lack realistic sourcepackages in sample data make this hard to see. I like what I do see. I think this is fine to land. We can fix issues that users report, or we see when the form is used with real data.

review: Approve (ui)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README'
2--- README 2010-04-06 20:07:30 +0000
3+++ README 2010-08-12 16:55:41 +0000
4@@ -1,4 +1,106 @@
5-This is the top level project, that supplies the infrastructure for testing,
6-and running launchpad.
7-
8-Documentation is in the doc directory or on the wiki.
9+====================
10+README for Launchpad
11+====================
12+
13+Launchpad is an open source suite of tools that help people and teams to work
14+together on software projects. Unlike many open source projects, Launchpad
15+isn't something you install and run yourself (although you are welcome to do
16+so), instead, contributors help make <https://launchpad.net> better.
17+
18+Launchpad is a project of Canonical <http://www.canonical.com> and has
19+received many contributions from many wonderful people
20+<https://dev.launchpad.net/Contributions>.
21+
22+If you want help using Launchpad, then please visit our help wiki at:
23+
24+ https://help.launchpad.net
25+
26+If you'd like to contribute to Launchpad, have a look at:
27+
28+ https://dev.launchpad.net
29+
30+Alternatively, have a poke around in the code, which you probably already know
31+how to get if you are reading this file.
32+
33+
34+Getting started
35+===============
36+
37+There's a full guide for getting up-and-running with a development Launchpad
38+environment at <https://dev.launchpad.net/Getting>. When you are ready to
39+submit a patch, please consult <https://dev.launchpad.net/PatchSubmission>.
40+
41+Our bug tracker is at <https://bugs.launchpad.net/launchpad/> and you can get
42+the source code any time by doing:
43+
44+ $ bzr branch lp:launchpad
45+
46+
47+Navigating the tree
48+-------------------
49+
50+The Launchpad tree is big, messy and changing. Sorry about that. Don't panic
51+though, it can sense fear. Keep a firm grip on `grep` and pay attention to
52+these important top-level folders:
53+
54+ bin/, utilities/
55+ Where you will find scripts intended for developers and admins. There's
56+ no rhyme or reason to what goes in bin/ and what goes in utilities/, so
57+ take a look in both. bin/ will be empty in a fresh checkout, the actual
58+ content lives in 'buildout-templates'.
59+
60+ configs/
61+ Configuration files for various kinds of Launchpad instances.
62+ 'development' and 'testrunner' are of particular interest to developers.
63+
64+ cronscripts/
65+ Scripts that are run on actual production instances of Launchpad as
66+ cronjobs.
67+
68+ daemons/
69+ Entry points for various daemons that form part of Launchpad
70+
71+ database/
72+ Our database schema, our sample data, and some other stuff that causes
73+ fear.
74+
75+ doc/
76+ General system-wide documentation. You can also find documentation on
77+ <https://dev.launchpad.net>, in docstrings and in doctests.
78+
79+ lib/
80+ Where the vast majority of the code lives, along with our templates, tests
81+ and the bits of our documentation that are written as doctests. 'lp' and
82+ 'canonical' are the two most interesting packages. Note that 'canonical'
83+ is deprecated in favour of 'lp'. To learn more about how the 'lp' package
84+ is laid out, take a look at its docstring.
85+
86+ Makefile
87+ Ahh, bliss. The Makefile has all sorts of goodies. If you spend any
88+ length of time hacking on Launchpad, you'll use it often. The most
89+ important targets are 'make clean', 'make compile', 'make schema', 'make
90+ run' and 'make run_all'.
91+
92+ scripts/
93+ Scripts that are run on actual production instances of Launchpad,
94+ generally triggered by some automatic process.
95+
96+
97+You can spend years hacking on Launchpad full-time and not know what all of
98+the files in the top-level directory are for. However, here's a guide to some
99+of the ones that come up from time to time.
100+
101+ buildout-templates/
102+ Templates that are generated into actual files, normally bin/ scripts,
103+ when buildout is run. If you want to change the behaviour of bin/test,
104+ look here.
105+
106+ bzrplugins/, optionalbzrplugins/
107+ Bazaar plugins used in running Launchpad.
108+
109+ sourcecode/
110+ A directory into which we symlink branches of some of Launchpad's
111+ dependencies. Don't ask.
112+
113+You never have to care about 'benchmarks', 'override-includes' or
114+'package-includes'.
115
116=== modified file 'lib/canonical/launchpad/emailtemplates/product-other-license.txt'
117--- lib/canonical/launchpad/emailtemplates/product-other-license.txt 2010-05-12 19:06:17 +0000
118+++ lib/canonical/launchpad/emailtemplates/product-other-license.txt 2010-08-12 16:55:41 +0000
119@@ -28,8 +28,9 @@
120 questions.
121
122 Sometimes new projects are licensed as 'Other/Open Source' because the
123-licensing decisions have not yet been made. If that is your situation we urge
124-you to update the licensing in Launchpad as soon as you make that choice.
125+licensing decisions have not yet been made. If that is your situation
126+we urge you to update the licensing in Launchpad as soon as you make
127+that choice.
128
129 If the license for your project needs to be corrected you can do so by
130 following the 'Change Details' link on your project's overview page.
131
132=== modified file 'lib/canonical/widgets/product.py'
133--- lib/canonical/widgets/product.py 2010-06-21 04:08:54 +0000
134+++ lib/canonical/widgets/product.py 2010-08-12 16:55:41 +0000
135@@ -316,6 +316,7 @@
136 self, 'license_info', self.license_info, IInputWidget,
137 prefix='field', value=initial_value,
138 context=field.context)
139+ self.source_package_release = None
140 # These will get filled in by _categorize(). They are the number of
141 # selected licenses in the category. The actual count doesn't matter,
142 # since if it's greater than 0 it will start opened. NOte that we
143
144=== modified file 'lib/canonical/widgets/templates/license.pt'
145--- lib/canonical/widgets/templates/license.pt 2009-07-17 17:59:07 +0000
146+++ lib/canonical/widgets/templates/license.pt 2010-08-12 16:55:41 +0000
147@@ -42,7 +42,7 @@
148 // the slider depends on whether there are any checked
149 // licenses in that category. A '0' means 'no'.
150 var arrow = Y.get(arrow_name);
151- if (arrow.getAttribute('count') == '0') {
152+ if (arrow.getAttribute('start_expanded') == '0') {
153 target.slide = Y.lazr.effects.slide_in(table_name);
154 }
155 else {
156@@ -76,6 +76,7 @@
157 target.slide.run();
158 }, target_name);
159 }
160+ make_slider({which: 'copyright'});
161 make_slider({which: 'recommended'});
162 make_slider({which: 'more'});
163 make_slider({which: 'deprecated'});
164@@ -136,6 +137,23 @@
165 //]]>
166 </script>
167 <div style="color: black">
168+ <tal:copyright condition="view/source_package_release">
169+ <a href="" id="copyright-expand" class="js-action">
170+ <img id="copyright-expand-arrow"
171+ src="/@@/treeCollapsed"
172+ title="Copyright info from source package"
173+ alt="Copyright info from source package"
174+ start_expanded="0"/>
175+ Copyright info from source package
176+ </a>
177+ <div id="copyright">
178+ <div
179+ tal:content="structure view/source_package_release/@@+copyright"
180+ style="overflow-x: hidden; overflow-y: auto;
181+ max-width: 60em; max-height: 32em; background: #f7f7f7"
182+ />
183+ </div>
184+ </tal:copyright>
185
186 Select the license(s) under which you release your project.
187 <div tal:condition="view/allow_pending_license"
188@@ -159,7 +177,7 @@
189 src="/@@/treeExpanded"
190 title="Recommended open source licenses"
191 alt="Recommended open source licenses"
192- tal:attributes="count view/recommended_count"/>
193+ tal:attributes="start_expanded view/recommended_count"/>
194 Recommended open source licenses
195 </a>
196 <input tal:replace="structure view/recommended" />
197@@ -168,7 +186,7 @@
198 src="/@@/treeCollapsed"
199 title="More open source licenses"
200 alt="More open source licenses"
201- tal:attributes="count view/more_count"/>
202+ tal:attributes="start_expanded view/more_count"/>
203 More open source licenses
204 </a>
205 <input tal:replace="structure view/more" />
206@@ -178,7 +196,7 @@
207 src="/@@/treeCollapsed"
208 title="Deprecated licenses"
209 alt="Deprecated licenses"
210- tal:attributes="count view/deprecated_count"/>
211+ tal:attributes="start_expanded view/deprecated_count"/>
212 Deprecated licenses
213 </a>
214 <input tal:replace="structure view/deprecated" />
215@@ -188,7 +206,7 @@
216 src="/@@/treeCollapsed"
217 title="Other choices"
218 alt="Other choices"
219- tal:attributes="count view/special_count"/>
220+ tal:attributes="start_expanded view/special_count"/>
221 Other choices
222 </a>
223 <input tal:replace="structure view/special" />
224
225=== modified file 'lib/lp/registry/browser/product.py'
226--- lib/lp/registry/browser/product.py 2010-08-12 16:55:39 +0000
227+++ lib/lp/registry/browser/product.py 2010-08-12 16:55:41 +0000
228@@ -83,6 +83,7 @@
229 from lp.registry.interfaces.pillar import IPillarNameSet
230 from lp.registry.interfaces.product import IProductReviewSearch, License
231 from lp.registry.interfaces.series import SeriesStatus
232+from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
233 from lp.registry.interfaces.product import (
234 IProduct, IProductSet, LicenseStatus)
235 from lp.registry.interfaces.productrelease import (
236@@ -1833,6 +1834,17 @@
237 return canonical_url(self.product)
238
239
240+def create_source_package_fields():
241+ return form.Fields(
242+ Choice(__name__='source_package_name',
243+ vocabulary='SourcePackageName',
244+ required=False),
245+ Choice(__name__='distroseries',
246+ vocabulary='DistroSeries',
247+ required=False),
248+ )
249+
250+
251 class ProjectAddStepOne(StepView):
252 """product/+new view class for creating a new project."""
253
254@@ -1849,6 +1861,19 @@
255 step_description = 'Project basics'
256 search_results_count = 0
257
258+ def setUpFields(self):
259+ """See `LaunchpadFormView`."""
260+ super(ProjectAddStepOne, self).setUpFields()
261+ self.form_fields = (
262+ self.form_fields +
263+ create_source_package_fields())
264+
265+ def setUpWidgets(self):
266+ """See `LaunchpadFormView`."""
267+ super(ProjectAddStepOne, self).setUpWidgets()
268+ self.widgets['source_package_name'].visible = False
269+ self.widgets['distroseries'].visible = False
270+
271 @property
272 def _return_url(self):
273 """This view is using the hidden _return_url field.
274@@ -1872,9 +1897,6 @@
275 def main_action(self, data):
276 """See `MultiStepView`."""
277 self.next_step = self._next_step
278- self.request.form['displayname'] = data['displayname']
279- self.request.form['name'] = data['name'].lower()
280- self.request.form['summary'] = data['summary']
281
282 # Make this a safe_action, so that the sourcepackage page can skip
283 # the first step with a link (GET request) providing form values.
284@@ -1887,7 +1909,6 @@
285 _field_names = ['displayname', 'name', 'title', 'summary',
286 'description', 'licenses', 'license_info',
287 ]
288- main_action_label = u'Complete Registration'
289 schema = IProduct
290 step_name = 'projectaddstep2'
291 template = ViewPageTemplateFile('../templates/product-new.pt')
292@@ -1901,6 +1922,15 @@
293 custom_widget('license_info', GhostWidget)
294
295 @property
296+ def main_action_label(self):
297+ if self.source_package_name is None:
298+ return u'Complete Registration'
299+ else:
300+ return u'Complete registration and link to %s package' % (
301+ self.source_package_name.name,
302+ )
303+
304+ @property
305 def _return_url(self):
306 """This view is using the hidden _return_url field.
307
308@@ -1921,7 +1951,8 @@
309 """See `LaunchpadFormView`."""
310 super(ProjectAddStepTwo, self).setUpFields()
311 self.form_fields = (self.form_fields +
312- self._createDisclaimMaintainerField())
313+ self._createDisclaimMaintainerField() +
314+ create_source_package_fields())
315
316 def _createDisclaimMaintainerField(self):
317 """Return a Bool field for disclaiming maintainer.
318@@ -1954,12 +1985,39 @@
319 "this will be the project's URL.")
320 self.widgets['displayname'].visible = False
321
322+ self.widgets['source_package_name'].visible = False
323+ self.widgets['distroseries'].visible = False
324+
325+ # Set the source_package_release attribute on the licenses
326+ # widget, so that the source package's copyright info can be
327+ # displayed.
328+ ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
329+ if self.source_package_name is not None:
330+ release_list = ubuntu.getCurrentSourceReleases(
331+ [self.source_package_name])
332+ if len(release_list) != 0:
333+ self.widgets['licenses'].source_package_release = (
334+ release_list.items()[0][1])
335+
336+ @property
337+ def source_package_name(self):
338+ # setUpWidgets() doesn't have access to the data dictionary,
339+ # so the source package name needs to be converted from a string
340+ # into an object here.
341+ package_name_string = self.request.form.get(
342+ 'field.source_package_name')
343+ if package_name_string is None:
344+ return None
345+ else:
346+ return getUtility(ISourcePackageNameSet).queryByName(
347+ package_name_string)
348+
349 @cachedproperty
350 def _search_string(self):
351 """Return the ORed terms to match."""
352- search_text = SPACE.join((self.request.form['name'],
353- self.request.form['displayname'],
354- self.request.form['summary']))
355+ search_text = SPACE.join((self.request.form['field.name'],
356+ self.request.form['field.displayname'],
357+ self.request.form['field.summary']))
358 # OR all the terms together.
359 return OR.join(search_text.split())
360
361@@ -1996,7 +2054,8 @@
362 def label(self):
363 """See `LaunchpadFormView`."""
364 return 'Register %s (%s) in Launchpad' % (
365- self.request.form['displayname'], self.request.form['name'])
366+ self.request.form['field.displayname'],
367+ self.request.form['field.name'])
368
369 def create_product(self, data):
370 """Create the product from the user data."""
371@@ -2020,11 +2079,24 @@
372 license_info=data['license_info'],
373 project=project)
374
375+ def link_source_package(self, product, data):
376+ if (data.get('distroseries') is not None
377+ and self.source_package_name is not None):
378+ source_package = data['distroseries'].getSourcePackage(
379+ self.source_package_name)
380+ source_package.setPackaging(
381+ product.development_focus, self.user)
382+ self.request.response.addInfoNotification(
383+ 'Linked %s project to %s source package.' % (
384+ product.displayname, self.source_package_name.name))
385+
386 def main_action(self, data):
387 """See `MultiStepView`."""
388 self.product = self.create_product(data)
389 self.notifyCommercialMailingList()
390 notify(ObjectCreatedEvent(self.product))
391+ self.link_source_package(self.product, data)
392+
393 if self._return_url is None:
394 self.next_url = canonical_url(self.product)
395 else:
396
397=== modified file 'lib/lp/registry/browser/sourcepackage.py'
398--- lib/lp/registry/browser/sourcepackage.py 2010-08-12 16:55:39 +0000
399+++ lib/lp/registry/browser/sourcepackage.py 2010-08-12 16:55:41 +0000
400@@ -66,10 +66,15 @@
401 from canonical.lazr.utils import smartquote
402
403
404-def get_register_upstream_url(source_package, return_url):
405+def get_register_upstream_url(source_package):
406 displayname = string.capwords(source_package.name.replace('-', ' '))
407+ distroseries_string = "%s/%s" % (
408+ source_package.distroseries.distribution.name,
409+ source_package.distroseries.name)
410 params = {
411- '_return_url': return_url,
412+ '_return_url': canonical_url(source_package),
413+ 'field.source_package_name': source_package.sourcepackagename.name,
414+ 'field.distroseries': distroseries_string,
415 'field.name': source_package.name,
416 'field.displayname': displayname,
417 'field.title': displayname,
418@@ -220,8 +225,7 @@
419
420 @property
421 def register_upstream_url(self):
422- return get_register_upstream_url(
423- self.context, return_url=self.request.getURL())
424+ return get_register_upstream_url(self.context)
425
426
427 class SourcePackageChangeUpstreamStepTwo(ReturnToReferrerMixin, StepView):
428@@ -526,8 +530,7 @@
429 return
430 elif upstream is self.register_upstream:
431 # The user wants to create a new project.
432- url = get_register_upstream_url(
433- self.context, return_url=self.request.getURL())
434+ url = get_register_upstream_url(self.context)
435 self.request.response.redirect(url)
436 return
437 self.context.setPackaging(upstream.development_focus, self.user)
438
439=== modified file 'lib/lp/registry/browser/tests/project-add-views.txt'
440--- lib/lp/registry/browser/tests/project-add-views.txt 2010-05-25 04:41:12 +0000
441+++ lib/lp/registry/browser/tests/project-add-views.txt 2010-08-12 16:55:41 +0000
442@@ -15,25 +15,24 @@
443 are forwarded in the form data to the second step. The title is also
444 forwarded, but is only required by the Zope machinery, not the view.
445
446- >>> form = {'field.actions.continue': 'Continue'}
447+ >>> from lp.registry.browser.product import ProjectAddStepOne
448+ >>> form = {
449+ ... 'field.actions.continue': 'Continue',
450+ ... 'field.__visited_steps__': ProjectAddStepOne.step_name,
451+ ... 'field.displayname': '',
452+ ... 'field.name': '',
453+ ... 'field.summary': '',
454+ ... }
455
456 >>> view = create_initialized_view(product_set, name='+new', form=form)
457- Traceback (most recent call last):
458- ...
459- KeyError: 'displayname'
460+ >>> for error in view.view.errors:
461+ ... print error
462+ ('displayname', 'Name', RequiredMissing())
463+ ('name', 'URL', RequiredMissing())
464+ ('summary', u'Summary', RequiredMissing())
465
466 >>> form['field.displayname'] = 'Snowdog'
467- >>> view = create_initialized_view(product_set, name='+new', form=form)
468- Traceback (most recent call last):
469- ...
470- KeyError: 'name'
471-
472 >>> form['field.name'] = 'snowdog'
473- >>> view = create_initialized_view(product_set, name='+new', form=form)
474- Traceback (most recent call last):
475- ...
476- KeyError: 'summary'
477-
478 >>> form['field.summary'] = 'By-tor and the Snowdog'
479 >>> view = create_initialized_view(product_set, name='+new', form=form)
480
481@@ -44,7 +43,6 @@
482 # steps individually.
483
484 >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
485- >>> from lp.registry.browser.product import ProjectAddStepOne
486
487 >>> form['field.__visited_steps__'] = ProjectAddStepOne.step_name
488 >>> request = LaunchpadTestRequest(form=form, method='POST')
489@@ -63,10 +61,12 @@
490
491 >>> from lp.registry.browser.product import ProjectAddStepTwo
492 >>> form = {
493- ... 'displayname': 'Snowdog',
494- ... 'name': 'snowdog',
495- ... 'title': 'The Snowdog',
496- ... 'summary': 'By-tor and the Snowdog',
497+ ... 'field.actions.continue': 'Continue',
498+ ... 'field.__visited_steps__': ProjectAddStepTwo.step_name,
499+ ... 'field.displayname': 'Snowdog',
500+ ... 'field.name': 'snowdog',
501+ ... 'field.title': 'The Snowdog',
502+ ... 'field.summary': 'By-tor and the Snowdog',
503 ... }
504
505 >>> request = LaunchpadTestRequest(form=form, method='POST')
506@@ -90,7 +90,7 @@
507 existing projects for possible matches. By tweaking the project summary, we
508 can see that there are search results available.
509
510- >>> form['summary'] = 'My Snowdog ate your Firefox'
511+ >>> form['field.summary'] = 'My Snowdog ate your Firefox'
512
513 >>> request = LaunchpadTestRequest(form=form, method='POST')
514 >>> view = ProjectAddStepTwo(product_set, request)
515@@ -229,9 +229,9 @@
516 questions.
517 <BLANKLINE>
518 Sometimes new projects are licensed as 'Other/Open Source' because the
519- licensing decisions have not yet been made. If that is your situation we u=
520- rge
521- you to update the licensing in Launchpad as soon as you make that choice.
522+ licensing decisions have not yet been made. If that is your situation
523+ we urge you to update the licensing in Launchpad as soon as you make
524+ that choice.
525 <BLANKLINE>
526 If the license for your project needs to be corrected you can do so by
527 following the 'Change Details' link on your project's overview page.
528
529=== modified file 'lib/lp/registry/browser/tests/test_sourcepackage_views.py'
530--- lib/lp/registry/browser/tests/test_sourcepackage_views.py 2010-08-12 16:55:39 +0000
531+++ lib/lp/registry/browser/tests/test_sourcepackage_views.py 2010-08-12 16:55:41 +0000
532@@ -9,11 +9,16 @@
533 import urllib
534
535 from zope.component import getUtility
536+from zope.interface import implements
537
538 from canonical.testing import DatabaseFunctionalLayer
539
540+
541 from lp.registry.browser.sourcepackage import get_register_upstream_url
542-from lp.registry.interfaces.distroseries import IDistroSeriesSet
543+from lp.registry.interfaces.distribution import IDistribution
544+from lp.registry.interfaces.distroseries import (
545+ IDistroSeries, IDistroSeriesSet)
546+from lp.registry.interfaces.sourcepackage import ISourcePackage
547 from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
548 from lp.testing import TestCaseWithFactory
549
550@@ -24,25 +29,35 @@
551 layer = DatabaseFunctionalLayer
552
553 def test_get_register_upstream_url_displayname(self):
554+ distroseries = self.factory.makeDistroRelease(
555+ distribution=self.factory.makeDistribution(name='zoobuntu'),
556+ name='walrus')
557 source_package = self.factory.makeSourcePackage(
558+ distroseries=distroseries,
559 sourcepackagename='python-super-package')
560- return_url = 'http://example.com/foo?a=b&c=d'
561- url = get_register_upstream_url(source_package, return_url)
562- expected_url = (
563- '/projects/+new?'
564- '_return_url='
565- 'http%3A%2F%2Fexample.com%2Ffoo%3Fa%3Db%26c%3Dd'
566- '&field.__visited_steps__=projectaddstep1'
567- '&field.actions.continue=Continue'
568+ url = get_register_upstream_url(source_package)
569+ expected_base = '/projects/+new'
570+ expected_params = [
571+ ('_return_url',
572+ 'http://launchpad.dev/zoobuntu/walrus/'
573+ '+source/python-super-package'),
574+ ('field.__visited_steps__', 'projectaddstep1'),
575+ ('field.actions.continue', 'Continue'),
576 # The sourcepackagename 'python-super-package' is split on
577 # the hyphens, and each word is capitalized.
578- '&field.displayname=Python+Super+Package'
579- '&field.name=python-super-package'
580- # The summary is empty, since the source package doesn't
581- # have a binary package release.
582- '&field.summary='
583- '&field.title=Python+Super+Package')
584- self.assertEqual(expected_url, url)
585+ ('field.displayname', 'Python Super Package'),
586+ ('field.distroseries', 'zoobuntu/walrus'),
587+ ('field.name', 'python-super-package'),
588+ # The summary is missing, since the source package doesn't
589+ # have a binary package release, and parse_qsl() excludes
590+ # empty params.
591+ ('field.source_package_name', 'python-super-package'),
592+ ('field.title', 'Python Super Package'),
593+ ]
594+ base, query = urllib.splitquery(url)
595+ params = cgi.parse_qsl(query)
596+ self.assertEqual((expected_base, expected_params),
597+ (base, params))
598
599 def test_get_register_upstream_url_summary(self):
600 test_publisher = SoyuzTestPublisher()
601@@ -57,15 +72,17 @@
602 # objects need to be reloaded.
603 distroseries = getUtility(IDistroSeriesSet).get(distroseries_id)
604 source_package = distroseries.getSourcePackage(source_package_name)
605- return_url = 'http://example.com/foo?a=b&c=d'
606- url = get_register_upstream_url(source_package, return_url)
607+ url = get_register_upstream_url(source_package)
608 expected_base = '/projects/+new'
609 expected_params = [
610- ('_return_url', 'http://example.com/foo?a=b&c=d'),
611+ ('_return_url',
612+ 'http://launchpad.dev/youbuntu/busy/+source/bonkers'),
613 ('field.__visited_steps__', 'projectaddstep1'),
614 ('field.actions.continue', 'Continue'),
615 ('field.displayname', 'Bonkers'),
616+ ('field.distroseries', 'youbuntu/busy'),
617 ('field.name', 'bonkers'),
618+ ('field.source_package_name', 'bonkers'),
619 ('field.summary', 'summary for flubber-bin\n'
620 + 'summary for flubber-lib'),
621 ('field.title', 'Bonkers'),
622@@ -77,32 +94,47 @@
623
624 def test_get_register_upstream_url_summary_duplicates(self):
625
626- class FakeDistroSeriesBinaryPackage:
627- def __init__(self, summary):
628- self.summary = summary
629-
630- class FakeDistributionSourcePackageRelease:
631- sample_binary_packages = [
632- FakeDistroSeriesBinaryPackage('summary for foo'),
633- FakeDistroSeriesBinaryPackage('summary for bar'),
634- FakeDistroSeriesBinaryPackage('summary for baz'),
635- FakeDistroSeriesBinaryPackage('summary for baz'),
636- ]
637-
638- class FakeSourcePackage:
639- name = 'foo'
640- releases = [FakeDistributionSourcePackageRelease()]
641-
642- source_package = FakeSourcePackage()
643- return_url = 'http://example.com/foo?a=b&c=d'
644- url = get_register_upstream_url(source_package, return_url)
645+ class Faker:
646+ # Fakes attributes easily.
647+ def __init__(self, **kw):
648+ self.__dict__.update(kw)
649+
650+ class FakeSourcePackage(Faker):
651+ # Interface necessary for canonical_url() call in
652+ # get_register_upstream_url().
653+ implements(ISourcePackage)
654+
655+ class FakeDistroSeries(Faker):
656+ implements(IDistroSeries)
657+
658+ class FakeDistribution(Faker):
659+ implements(IDistribution)
660+
661+ releases = Faker(sample_binary_packages=[
662+ Faker(summary='summary for foo'),
663+ Faker(summary='summary for bar'),
664+ Faker(summary='summary for baz'),
665+ Faker(summary='summary for baz'),
666+ ])
667+ source_package = FakeSourcePackage(
668+ name='foo',
669+ sourcepackagename=Faker(name='foo'),
670+ distroseries=FakeDistroSeries(
671+ name='walrus',
672+ distribution=FakeDistribution(name='zoobuntu')),
673+ releases=[releases])
674+
675+ url = get_register_upstream_url(source_package)
676 expected_base = '/projects/+new'
677 expected_params = [
678- ('_return_url', 'http://example.com/foo?a=b&c=d'),
679+ ('_return_url',
680+ 'http://launchpad.dev/zoobuntu/walrus/+source/foo'),
681 ('field.__visited_steps__', 'projectaddstep1'),
682 ('field.actions.continue', 'Continue'),
683 ('field.displayname', 'Foo'),
684+ ('field.distroseries', 'zoobuntu/walrus'),
685 ('field.name', 'foo'),
686+ ('field.source_package_name', 'foo'),
687 ('field.summary', 'summary for bar\n'
688 + 'summary for baz\n'
689 + 'summary for foo'),
690
691=== modified file 'lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt'
692--- lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt 2010-08-12 16:55:39 +0000
693+++ lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt 2010-08-12 16:55:41 +0000
694@@ -84,11 +84,13 @@
695 ... 'Register the upstream project').selected = True
696 >>> user_browser.getControl("Link to Upstream Project").click()
697 >>> print user_browser.url.replace('&', '\n&')
698- http://launchpad.dev/projects/+new?_return_url=http...%2Bindex
699+ http://launchpad.dev/projects/+new?_return_url=http...%2Bsource%2Fbonkers
700 &field.__visited_steps__=projectaddstep1
701 &field.actions.continue=Continue
702 &field.displayname=Bonkers
703+ &field.distroseries=youbuntu%2Fbusy
704 &field.name=bonkers
705+ &field.source_package_name=bonkers
706 &field.summary=summary+for+flubber-bin%0Asummary+for+flubber-lib
707 &field.title=Bonkers
708 >>> print user_browser.getControl(name='field.name').value
709@@ -118,11 +120,13 @@
710
711 >>> user_browser.getLink("Register the upstream project").click()
712 >>> print user_browser.url.replace('&', '\n&')
713- http://launchpad.dev/projects/+new?_return_url=http...%2Bedit-packaging
714+ http://launchpad.dev/projects/+new?_return_url=http...%2Bsource%2Fbonkers
715 &field.__visited_steps__=projectaddstep1
716 &field.actions.continue=Continue
717 &field.displayname=Bonkers
718+ &field.distroseries=youbuntu%2Fbusy
719 &field.name=bonkers
720+ &field.source_package_name=bonkers
721 &field.summary=summary+for+flubber-bin%0Asummary+for+flubber-lib
722 &field.title=Bonkers
723 >>> print user_browser.getControl(name='field.name').value
724@@ -140,9 +144,19 @@
725
726 If there are no problems with the prefilled data, then the license
727 just needs to be selected. The user will then be redirected back
728-to the source package page so that it can be linked.
729+to the source package page and an informational message will be displayed.
730
731 >>> user_browser.getControl(name='field.licenses').value = ['BSD']
732- >>> user_browser.getControl("Complete Registration").click()
733+ >>> user_browser.getControl(
734+ ... "Complete registration and link to bonkers package").click()
735 >>> print user_browser.url
736- http://launchpad.dev/youbuntu/busy/+source/bonkers/+edit-packaging
737+ http://launchpad.dev/youbuntu/busy/+source/bonkers
738+ >>> for tag in find_tags_by_class(
739+ ... user_browser.contents, 'informational message'):
740+ ... print extract_text(tag)
741+ Linked Bonkers project to bonkers source package.
742+ >>> print extract_text(
743+ ... find_tag_by_id(user_browser.contents, 'upstreams'))
744+ Bonkers &rArr; trunk
745+ Change upstream link
746+ Remove upstream link...
747
748=== modified file 'lib/lp/registry/templates/product-new.pt'
749--- lib/lp/registry/templates/product-new.pt 2010-05-12 19:06:17 +0000
750+++ lib/lp/registry/templates/product-new.pt 2010-08-12 16:55:41 +0000
751@@ -299,8 +299,8 @@
752 <img src="/@@/info" />
753 There are similar projects already registered in Launchpad.
754 Is project
755- <strong><tal:displayname tal:replace="view/request/displayname" />
756- (<tal:name tal:replace="view/request/name" />)</strong>
757+ <strong><tal:displayname tal:replace="view/request/field.displayname" />
758+ (<tal:name tal:replace="view/request/field.name" />)</strong>
759 one of these?
760 </div>
761
762@@ -326,8 +326,8 @@
763 tal:condition="view/search_results_count"
764 >Registration details</h3>
765 Select the licenses for project
766- <strong><tal:displayname tal:replace="view/request/displayname" />
767- (<tal:name tal:replace="view/request/name" />)</strong>
768+ <strong><tal:displayname tal:replace="view/request/field.displayname" />
769+ (<tal:name tal:replace="view/request/field.name" />)</strong>
770 and complete the registration. You may also update the project's
771 title and summary.
772 </div>

Subscribers

People subscribed via source and target branches

to status/vote changes: