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
=== modified file 'README'
--- README 2010-04-06 20:07:30 +0000
+++ README 2010-08-12 16:55:41 +0000
@@ -1,4 +1,106 @@
1This is the top level project, that supplies the infrastructure for testing,1====================
2and running launchpad.2README for Launchpad
33====================
4Documentation is in the doc directory or on the wiki.4
5Launchpad is an open source suite of tools that help people and teams to work
6together on software projects. Unlike many open source projects, Launchpad
7isn't something you install and run yourself (although you are welcome to do
8so), instead, contributors help make <https://launchpad.net> better.
9
10Launchpad is a project of Canonical <http://www.canonical.com> and has
11received many contributions from many wonderful people
12<https://dev.launchpad.net/Contributions>.
13
14If you want help using Launchpad, then please visit our help wiki at:
15
16 https://help.launchpad.net
17
18If you'd like to contribute to Launchpad, have a look at:
19
20 https://dev.launchpad.net
21
22Alternatively, have a poke around in the code, which you probably already know
23how to get if you are reading this file.
24
25
26Getting started
27===============
28
29There's a full guide for getting up-and-running with a development Launchpad
30environment at <https://dev.launchpad.net/Getting>. When you are ready to
31submit a patch, please consult <https://dev.launchpad.net/PatchSubmission>.
32
33Our bug tracker is at <https://bugs.launchpad.net/launchpad/> and you can get
34the source code any time by doing:
35
36 $ bzr branch lp:launchpad
37
38
39Navigating the tree
40-------------------
41
42The Launchpad tree is big, messy and changing. Sorry about that. Don't panic
43though, it can sense fear. Keep a firm grip on `grep` and pay attention to
44these important top-level folders:
45
46 bin/, utilities/
47 Where you will find scripts intended for developers and admins. There's
48 no rhyme or reason to what goes in bin/ and what goes in utilities/, so
49 take a look in both. bin/ will be empty in a fresh checkout, the actual
50 content lives in 'buildout-templates'.
51
52 configs/
53 Configuration files for various kinds of Launchpad instances.
54 'development' and 'testrunner' are of particular interest to developers.
55
56 cronscripts/
57 Scripts that are run on actual production instances of Launchpad as
58 cronjobs.
59
60 daemons/
61 Entry points for various daemons that form part of Launchpad
62
63 database/
64 Our database schema, our sample data, and some other stuff that causes
65 fear.
66
67 doc/
68 General system-wide documentation. You can also find documentation on
69 <https://dev.launchpad.net>, in docstrings and in doctests.
70
71 lib/
72 Where the vast majority of the code lives, along with our templates, tests
73 and the bits of our documentation that are written as doctests. 'lp' and
74 'canonical' are the two most interesting packages. Note that 'canonical'
75 is deprecated in favour of 'lp'. To learn more about how the 'lp' package
76 is laid out, take a look at its docstring.
77
78 Makefile
79 Ahh, bliss. The Makefile has all sorts of goodies. If you spend any
80 length of time hacking on Launchpad, you'll use it often. The most
81 important targets are 'make clean', 'make compile', 'make schema', 'make
82 run' and 'make run_all'.
83
84 scripts/
85 Scripts that are run on actual production instances of Launchpad,
86 generally triggered by some automatic process.
87
88
89You can spend years hacking on Launchpad full-time and not know what all of
90the files in the top-level directory are for. However, here's a guide to some
91of the ones that come up from time to time.
92
93 buildout-templates/
94 Templates that are generated into actual files, normally bin/ scripts,
95 when buildout is run. If you want to change the behaviour of bin/test,
96 look here.
97
98 bzrplugins/, optionalbzrplugins/
99 Bazaar plugins used in running Launchpad.
100
101 sourcecode/
102 A directory into which we symlink branches of some of Launchpad's
103 dependencies. Don't ask.
104
105You never have to care about 'benchmarks', 'override-includes' or
106'package-includes'.
5107
=== modified file 'lib/canonical/launchpad/emailtemplates/product-other-license.txt'
--- lib/canonical/launchpad/emailtemplates/product-other-license.txt 2010-05-12 19:06:17 +0000
+++ lib/canonical/launchpad/emailtemplates/product-other-license.txt 2010-08-12 16:55:41 +0000
@@ -28,8 +28,9 @@
28questions.28questions.
2929
30Sometimes new projects are licensed as 'Other/Open Source' because the30Sometimes new projects are licensed as 'Other/Open Source' because the
31licensing decisions have not yet been made. If that is your situation we urge31licensing decisions have not yet been made. If that is your situation
32you to update the licensing in Launchpad as soon as you make that choice.32we urge you to update the licensing in Launchpad as soon as you make
33that choice.
3334
34If the license for your project needs to be corrected you can do so by35If the license for your project needs to be corrected you can do so by
35following the 'Change Details' link on your project's overview page.36following the 'Change Details' link on your project's overview page.
3637
=== 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-12 16:55:41 +0000
@@ -316,6 +316,7 @@
316 self, 'license_info', self.license_info, IInputWidget,316 self, 'license_info', self.license_info, IInputWidget,
317 prefix='field', value=initial_value,317 prefix='field', value=initial_value,
318 context=field.context)318 context=field.context)
319 self.source_package_release = None
319 # These will get filled in by _categorize(). They are the number of320 # These will get filled in by _categorize(). They are the number of
320 # selected licenses in the category. The actual count doesn't matter,321 # selected licenses in the category. The actual count doesn't matter,
321 # since if it's greater than 0 it will start opened. NOte that we322 # since if it's greater than 0 it will start opened. NOte that we
322323
=== 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-12 16:55:41 +0000
@@ -42,7 +42,7 @@
42 // the slider depends on whether there are any checked42 // the slider depends on whether there are any checked
43 // licenses in that category. A '0' means 'no'.43 // licenses in that category. A '0' means 'no'.
44 var arrow = Y.get(arrow_name);44 var arrow = Y.get(arrow_name);
45 if (arrow.getAttribute('count') == '0') {45 if (arrow.getAttribute('start_expanded') == '0') {
46 target.slide = Y.lazr.effects.slide_in(table_name);46 target.slide = Y.lazr.effects.slide_in(table_name);
47 }47 }
48 else {48 else {
@@ -76,6 +76,7 @@
76 target.slide.run();76 target.slide.run();
77 }, target_name);77 }, target_name);
78 }78 }
79 make_slider({which: 'copyright'});
79 make_slider({which: 'recommended'});80 make_slider({which: 'recommended'});
80 make_slider({which: 'more'});81 make_slider({which: 'more'});
81 make_slider({which: 'deprecated'});82 make_slider({which: 'deprecated'});
@@ -136,6 +137,23 @@
136//]]>137//]]>
137</script>138</script>
138<div style="color: black">139<div style="color: black">
140 <tal:copyright condition="view/source_package_release">
141 <a href="" id="copyright-expand" class="js-action">
142 <img id="copyright-expand-arrow"
143 src="/@@/treeCollapsed"
144 title="Copyright info from source package"
145 alt="Copyright info from source package"
146 start_expanded="0"/>
147 Copyright info from source package
148 </a>
149 <div id="copyright">
150 <div
151 tal:content="structure view/source_package_release/@@+copyright"
152 style="overflow-x: hidden; overflow-y: auto;
153 max-width: 60em; max-height: 32em; background: #f7f7f7"
154 />
155 </div>
156 </tal:copyright>
139157
140 Select the license(s) under which you release your project.158 Select the license(s) under which you release your project.
141 <div tal:condition="view/allow_pending_license"159 <div tal:condition="view/allow_pending_license"
@@ -159,7 +177,7 @@
159 src="/@@/treeExpanded"177 src="/@@/treeExpanded"
160 title="Recommended open source licenses"178 title="Recommended open source licenses"
161 alt="Recommended open source licenses"179 alt="Recommended open source licenses"
162 tal:attributes="count view/recommended_count"/>180 tal:attributes="start_expanded view/recommended_count"/>
163 Recommended open source licenses181 Recommended open source licenses
164 </a>182 </a>
165 <input tal:replace="structure view/recommended" />183 <input tal:replace="structure view/recommended" />
@@ -168,7 +186,7 @@
168 src="/@@/treeCollapsed"186 src="/@@/treeCollapsed"
169 title="More open source licenses"187 title="More open source licenses"
170 alt="More open source licenses"188 alt="More open source licenses"
171 tal:attributes="count view/more_count"/>189 tal:attributes="start_expanded view/more_count"/>
172 More open source licenses190 More open source licenses
173 </a>191 </a>
174 <input tal:replace="structure view/more" />192 <input tal:replace="structure view/more" />
@@ -178,7 +196,7 @@
178 src="/@@/treeCollapsed"196 src="/@@/treeCollapsed"
179 title="Deprecated licenses"197 title="Deprecated licenses"
180 alt="Deprecated licenses"198 alt="Deprecated licenses"
181 tal:attributes="count view/deprecated_count"/>199 tal:attributes="start_expanded view/deprecated_count"/>
182 Deprecated licenses200 Deprecated licenses
183 </a>201 </a>
184 <input tal:replace="structure view/deprecated" />202 <input tal:replace="structure view/deprecated" />
@@ -188,7 +206,7 @@
188 src="/@@/treeCollapsed"206 src="/@@/treeCollapsed"
189 title="Other choices"207 title="Other choices"
190 alt="Other choices"208 alt="Other choices"
191 tal:attributes="count view/special_count"/>209 tal:attributes="start_expanded view/special_count"/>
192 Other choices210 Other choices
193 </a>211 </a>
194 <input tal:replace="structure view/special" />212 <input tal:replace="structure view/special" />
195213
=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py 2010-08-12 16:55:39 +0000
+++ lib/lp/registry/browser/product.py 2010-08-12 16:55:41 +0000
@@ -83,6 +83,7 @@
83from lp.registry.interfaces.pillar import IPillarNameSet83from lp.registry.interfaces.pillar import IPillarNameSet
84from lp.registry.interfaces.product import IProductReviewSearch, License84from lp.registry.interfaces.product import IProductReviewSearch, License
85from lp.registry.interfaces.series import SeriesStatus85from lp.registry.interfaces.series import SeriesStatus
86from lp.registry.interfaces.sourcepackagename import ISourcePackageNameSet
86from lp.registry.interfaces.product import (87from lp.registry.interfaces.product import (
87 IProduct, IProductSet, LicenseStatus)88 IProduct, IProductSet, LicenseStatus)
88from lp.registry.interfaces.productrelease import (89from lp.registry.interfaces.productrelease import (
@@ -1833,6 +1834,17 @@
1833 return canonical_url(self.product)1834 return canonical_url(self.product)
18341835
18351836
1837def create_source_package_fields():
1838 return form.Fields(
1839 Choice(__name__='source_package_name',
1840 vocabulary='SourcePackageName',
1841 required=False),
1842 Choice(__name__='distroseries',
1843 vocabulary='DistroSeries',
1844 required=False),
1845 )
1846
1847
1836class ProjectAddStepOne(StepView):1848class ProjectAddStepOne(StepView):
1837 """product/+new view class for creating a new project."""1849 """product/+new view class for creating a new project."""
18381850
@@ -1849,6 +1861,19 @@
1849 step_description = 'Project basics'1861 step_description = 'Project basics'
1850 search_results_count = 01862 search_results_count = 0
18511863
1864 def setUpFields(self):
1865 """See `LaunchpadFormView`."""
1866 super(ProjectAddStepOne, self).setUpFields()
1867 self.form_fields = (
1868 self.form_fields +
1869 create_source_package_fields())
1870
1871 def setUpWidgets(self):
1872 """See `LaunchpadFormView`."""
1873 super(ProjectAddStepOne, self).setUpWidgets()
1874 self.widgets['source_package_name'].visible = False
1875 self.widgets['distroseries'].visible = False
1876
1852 @property1877 @property
1853 def _return_url(self):1878 def _return_url(self):
1854 """This view is using the hidden _return_url field.1879 """This view is using the hidden _return_url field.
@@ -1872,9 +1897,6 @@
1872 def main_action(self, data):1897 def main_action(self, data):
1873 """See `MultiStepView`."""1898 """See `MultiStepView`."""
1874 self.next_step = self._next_step1899 self.next_step = self._next_step
1875 self.request.form['displayname'] = data['displayname']
1876 self.request.form['name'] = data['name'].lower()
1877 self.request.form['summary'] = data['summary']
18781900
1879 # Make this a safe_action, so that the sourcepackage page can skip1901 # Make this a safe_action, so that the sourcepackage page can skip
1880 # the first step with a link (GET request) providing form values.1902 # the first step with a link (GET request) providing form values.
@@ -1887,7 +1909,6 @@
1887 _field_names = ['displayname', 'name', 'title', 'summary',1909 _field_names = ['displayname', 'name', 'title', 'summary',
1888 'description', 'licenses', 'license_info',1910 'description', 'licenses', 'license_info',
1889 ]1911 ]
1890 main_action_label = u'Complete Registration'
1891 schema = IProduct1912 schema = IProduct
1892 step_name = 'projectaddstep2'1913 step_name = 'projectaddstep2'
1893 template = ViewPageTemplateFile('../templates/product-new.pt')1914 template = ViewPageTemplateFile('../templates/product-new.pt')
@@ -1901,6 +1922,15 @@
1901 custom_widget('license_info', GhostWidget)1922 custom_widget('license_info', GhostWidget)
19021923
1903 @property1924 @property
1925 def main_action_label(self):
1926 if self.source_package_name is None:
1927 return u'Complete Registration'
1928 else:
1929 return u'Complete registration and link to %s package' % (
1930 self.source_package_name.name,
1931 )
1932
1933 @property
1904 def _return_url(self):1934 def _return_url(self):
1905 """This view is using the hidden _return_url field.1935 """This view is using the hidden _return_url field.
19061936
@@ -1921,7 +1951,8 @@
1921 """See `LaunchpadFormView`."""1951 """See `LaunchpadFormView`."""
1922 super(ProjectAddStepTwo, self).setUpFields()1952 super(ProjectAddStepTwo, self).setUpFields()
1923 self.form_fields = (self.form_fields +1953 self.form_fields = (self.form_fields +
1924 self._createDisclaimMaintainerField())1954 self._createDisclaimMaintainerField() +
1955 create_source_package_fields())
19251956
1926 def _createDisclaimMaintainerField(self):1957 def _createDisclaimMaintainerField(self):
1927 """Return a Bool field for disclaiming maintainer.1958 """Return a Bool field for disclaiming maintainer.
@@ -1954,12 +1985,39 @@
1954 "this will be the project's URL.")1985 "this will be the project's URL.")
1955 self.widgets['displayname'].visible = False1986 self.widgets['displayname'].visible = False
19561987
1988 self.widgets['source_package_name'].visible = False
1989 self.widgets['distroseries'].visible = False
1990
1991 # Set the source_package_release attribute on the licenses
1992 # widget, so that the source package's copyright info can be
1993 # displayed.
1994 ubuntu = getUtility(ILaunchpadCelebrities).ubuntu
1995 if self.source_package_name is not None:
1996 release_list = ubuntu.getCurrentSourceReleases(
1997 [self.source_package_name])
1998 if len(release_list) != 0:
1999 self.widgets['licenses'].source_package_release = (
2000 release_list.items()[0][1])
2001
2002 @property
2003 def source_package_name(self):
2004 # setUpWidgets() doesn't have access to the data dictionary,
2005 # so the source package name needs to be converted from a string
2006 # into an object here.
2007 package_name_string = self.request.form.get(
2008 'field.source_package_name')
2009 if package_name_string is None:
2010 return None
2011 else:
2012 return getUtility(ISourcePackageNameSet).queryByName(
2013 package_name_string)
2014
1957 @cachedproperty2015 @cachedproperty
1958 def _search_string(self):2016 def _search_string(self):
1959 """Return the ORed terms to match."""2017 """Return the ORed terms to match."""
1960 search_text = SPACE.join((self.request.form['name'],2018 search_text = SPACE.join((self.request.form['field.name'],
1961 self.request.form['displayname'],2019 self.request.form['field.displayname'],
1962 self.request.form['summary']))2020 self.request.form['field.summary']))
1963 # OR all the terms together.2021 # OR all the terms together.
1964 return OR.join(search_text.split())2022 return OR.join(search_text.split())
19652023
@@ -1996,7 +2054,8 @@
1996 def label(self):2054 def label(self):
1997 """See `LaunchpadFormView`."""2055 """See `LaunchpadFormView`."""
1998 return 'Register %s (%s) in Launchpad' % (2056 return 'Register %s (%s) in Launchpad' % (
1999 self.request.form['displayname'], self.request.form['name'])2057 self.request.form['field.displayname'],
2058 self.request.form['field.name'])
20002059
2001 def create_product(self, data):2060 def create_product(self, data):
2002 """Create the product from the user data."""2061 """Create the product from the user data."""
@@ -2020,11 +2079,24 @@
2020 license_info=data['license_info'],2079 license_info=data['license_info'],
2021 project=project)2080 project=project)
20222081
2082 def link_source_package(self, product, data):
2083 if (data.get('distroseries') is not None
2084 and self.source_package_name is not None):
2085 source_package = data['distroseries'].getSourcePackage(
2086 self.source_package_name)
2087 source_package.setPackaging(
2088 product.development_focus, self.user)
2089 self.request.response.addInfoNotification(
2090 'Linked %s project to %s source package.' % (
2091 product.displayname, self.source_package_name.name))
2092
2023 def main_action(self, data):2093 def main_action(self, data):
2024 """See `MultiStepView`."""2094 """See `MultiStepView`."""
2025 self.product = self.create_product(data)2095 self.product = self.create_product(data)
2026 self.notifyCommercialMailingList()2096 self.notifyCommercialMailingList()
2027 notify(ObjectCreatedEvent(self.product))2097 notify(ObjectCreatedEvent(self.product))
2098 self.link_source_package(self.product, data)
2099
2028 if self._return_url is None:2100 if self._return_url is None:
2029 self.next_url = canonical_url(self.product)2101 self.next_url = canonical_url(self.product)
2030 else:2102 else:
20312103
=== modified file 'lib/lp/registry/browser/sourcepackage.py'
--- lib/lp/registry/browser/sourcepackage.py 2010-08-12 16:55:39 +0000
+++ lib/lp/registry/browser/sourcepackage.py 2010-08-12 16:55:41 +0000
@@ -66,10 +66,15 @@
66from canonical.lazr.utils import smartquote66from canonical.lazr.utils import smartquote
6767
6868
69def get_register_upstream_url(source_package, return_url):69def get_register_upstream_url(source_package):
70 displayname = string.capwords(source_package.name.replace('-', ' '))70 displayname = string.capwords(source_package.name.replace('-', ' '))
71 distroseries_string = "%s/%s" % (
72 source_package.distroseries.distribution.name,
73 source_package.distroseries.name)
71 params = {74 params = {
72 '_return_url': return_url,75 '_return_url': canonical_url(source_package),
76 'field.source_package_name': source_package.sourcepackagename.name,
77 'field.distroseries': distroseries_string,
73 'field.name': source_package.name,78 'field.name': source_package.name,
74 'field.displayname': displayname,79 'field.displayname': displayname,
75 'field.title': displayname,80 'field.title': displayname,
@@ -220,8 +225,7 @@
220225
221 @property226 @property
222 def register_upstream_url(self):227 def register_upstream_url(self):
223 return get_register_upstream_url(228 return get_register_upstream_url(self.context)
224 self.context, return_url=self.request.getURL())
225229
226230
227class SourcePackageChangeUpstreamStepTwo(ReturnToReferrerMixin, StepView):231class SourcePackageChangeUpstreamStepTwo(ReturnToReferrerMixin, StepView):
@@ -526,8 +530,7 @@
526 return530 return
527 elif upstream is self.register_upstream:531 elif upstream is self.register_upstream:
528 # The user wants to create a new project.532 # The user wants to create a new project.
529 url = get_register_upstream_url(533 url = get_register_upstream_url(self.context)
530 self.context, return_url=self.request.getURL())
531 self.request.response.redirect(url)534 self.request.response.redirect(url)
532 return535 return
533 self.context.setPackaging(upstream.development_focus, self.user)536 self.context.setPackaging(upstream.development_focus, self.user)
534537
=== 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-12 16:55:41 +0000
@@ -15,25 +15,24 @@
15are forwarded in the form data to the second step. The title is also15are forwarded in the form data to the second step. The title is also
16forwarded, but is only required by the Zope machinery, not the view.16forwarded, but is only required by the Zope machinery, not the view.
1717
18 >>> form = {'field.actions.continue': 'Continue'}18 >>> from lp.registry.browser.product import ProjectAddStepOne
19 >>> form = {
20 ... 'field.actions.continue': 'Continue',
21 ... 'field.__visited_steps__': ProjectAddStepOne.step_name,
22 ... 'field.displayname': '',
23 ... 'field.name': '',
24 ... 'field.summary': '',
25 ... }
1926
20 >>> view = create_initialized_view(product_set, name='+new', form=form)27 >>> view = create_initialized_view(product_set, name='+new', form=form)
21 Traceback (most recent call last):28 >>> for error in view.view.errors:
22 ...29 ... print error
23 KeyError: 'displayname'30 ('displayname', 'Name', RequiredMissing())
31 ('name', 'URL', RequiredMissing())
32 ('summary', u'Summary', RequiredMissing())
2433
25 >>> form['field.displayname'] = 'Snowdog'34 >>> form['field.displayname'] = 'Snowdog'
26 >>> view = create_initialized_view(product_set, name='+new', form=form)
27 Traceback (most recent call last):
28 ...
29 KeyError: 'name'
30
31 >>> form['field.name'] = 'snowdog'35 >>> form['field.name'] = 'snowdog'
32 >>> view = create_initialized_view(product_set, name='+new', form=form)
33 Traceback (most recent call last):
34 ...
35 KeyError: 'summary'
36
37 >>> form['field.summary'] = 'By-tor and the Snowdog'36 >>> form['field.summary'] = 'By-tor and the Snowdog'
38 >>> view = create_initialized_view(product_set, name='+new', form=form)37 >>> view = create_initialized_view(product_set, name='+new', form=form)
3938
@@ -44,7 +43,6 @@
44 # steps individually.43 # steps individually.
4544
46 >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest45 >>> from canonical.launchpad.webapp.servers import LaunchpadTestRequest
47 >>> from lp.registry.browser.product import ProjectAddStepOne
4846
49 >>> form['field.__visited_steps__'] = ProjectAddStepOne.step_name47 >>> form['field.__visited_steps__'] = ProjectAddStepOne.step_name
50 >>> request = LaunchpadTestRequest(form=form, method='POST')48 >>> request = LaunchpadTestRequest(form=form, method='POST')
@@ -63,10 +61,12 @@
6361
64 >>> from lp.registry.browser.product import ProjectAddStepTwo62 >>> from lp.registry.browser.product import ProjectAddStepTwo
65 >>> form = {63 >>> form = {
66 ... 'displayname': 'Snowdog',64 ... 'field.actions.continue': 'Continue',
67 ... 'name': 'snowdog',65 ... 'field.__visited_steps__': ProjectAddStepTwo.step_name,
68 ... 'title': 'The Snowdog',66 ... 'field.displayname': 'Snowdog',
69 ... 'summary': 'By-tor and the Snowdog',67 ... 'field.name': 'snowdog',
68 ... 'field.title': 'The Snowdog',
69 ... 'field.summary': 'By-tor and the Snowdog',
70 ... }70 ... }
7171
72 >>> request = LaunchpadTestRequest(form=form, method='POST')72 >>> request = LaunchpadTestRequest(form=form, method='POST')
@@ -90,7 +90,7 @@
90existing projects for possible matches. By tweaking the project summary, we90existing projects for possible matches. By tweaking the project summary, we
91can see that there are search results available.91can see that there are search results available.
9292
93 >>> form['summary'] = 'My Snowdog ate your Firefox'93 >>> form['field.summary'] = 'My Snowdog ate your Firefox'
9494
95 >>> request = LaunchpadTestRequest(form=form, method='POST')95 >>> request = LaunchpadTestRequest(form=form, method='POST')
96 >>> view = ProjectAddStepTwo(product_set, request)96 >>> view = ProjectAddStepTwo(product_set, request)
@@ -229,9 +229,9 @@
229 questions.229 questions.
230 <BLANKLINE>230 <BLANKLINE>
231 Sometimes new projects are licensed as 'Other/Open Source' because the231 Sometimes new projects are licensed as 'Other/Open Source' because the
232 licensing decisions have not yet been made. If that is your situation we u=232 licensing decisions have not yet been made. If that is your situation
233 rge233 we urge you to update the licensing in Launchpad as soon as you make
234 you to update the licensing in Launchpad as soon as you make that choice.234 that choice.
235 <BLANKLINE>235 <BLANKLINE>
236 If the license for your project needs to be corrected you can do so by236 If the license for your project needs to be corrected you can do so by
237 following the 'Change Details' link on your project's overview page.237 following the 'Change Details' link on your project's overview page.
238238
=== modified file 'lib/lp/registry/browser/tests/test_sourcepackage_views.py'
--- lib/lp/registry/browser/tests/test_sourcepackage_views.py 2010-08-12 16:55:39 +0000
+++ lib/lp/registry/browser/tests/test_sourcepackage_views.py 2010-08-12 16:55:41 +0000
@@ -9,11 +9,16 @@
9import urllib9import urllib
1010
11from zope.component import getUtility11from zope.component import getUtility
12from zope.interface import implements
1213
13from canonical.testing import DatabaseFunctionalLayer14from canonical.testing import DatabaseFunctionalLayer
1415
16
15from lp.registry.browser.sourcepackage import get_register_upstream_url17from lp.registry.browser.sourcepackage import get_register_upstream_url
16from lp.registry.interfaces.distroseries import IDistroSeriesSet18from lp.registry.interfaces.distribution import IDistribution
19from lp.registry.interfaces.distroseries import (
20 IDistroSeries, IDistroSeriesSet)
21from lp.registry.interfaces.sourcepackage import ISourcePackage
17from lp.soyuz.tests.test_publishing import SoyuzTestPublisher22from lp.soyuz.tests.test_publishing import SoyuzTestPublisher
18from lp.testing import TestCaseWithFactory23from lp.testing import TestCaseWithFactory
1924
@@ -24,25 +29,35 @@
24 layer = DatabaseFunctionalLayer29 layer = DatabaseFunctionalLayer
2530
26 def test_get_register_upstream_url_displayname(self):31 def test_get_register_upstream_url_displayname(self):
32 distroseries = self.factory.makeDistroRelease(
33 distribution=self.factory.makeDistribution(name='zoobuntu'),
34 name='walrus')
27 source_package = self.factory.makeSourcePackage(35 source_package = self.factory.makeSourcePackage(
36 distroseries=distroseries,
28 sourcepackagename='python-super-package')37 sourcepackagename='python-super-package')
29 return_url = 'http://example.com/foo?a=b&c=d'38 url = get_register_upstream_url(source_package)
30 url = get_register_upstream_url(source_package, return_url)39 expected_base = '/projects/+new'
31 expected_url = (40 expected_params = [
32 '/projects/+new?'41 ('_return_url',
33 '_return_url='42 'http://launchpad.dev/zoobuntu/walrus/'
34 'http%3A%2F%2Fexample.com%2Ffoo%3Fa%3Db%26c%3Dd'43 '+source/python-super-package'),
35 '&field.__visited_steps__=projectaddstep1'44 ('field.__visited_steps__', 'projectaddstep1'),
36 '&field.actions.continue=Continue'45 ('field.actions.continue', 'Continue'),
37 # The sourcepackagename 'python-super-package' is split on46 # The sourcepackagename 'python-super-package' is split on
38 # the hyphens, and each word is capitalized.47 # the hyphens, and each word is capitalized.
39 '&field.displayname=Python+Super+Package'48 ('field.displayname', 'Python Super Package'),
40 '&field.name=python-super-package'49 ('field.distroseries', 'zoobuntu/walrus'),
41 # The summary is empty, since the source package doesn't50 ('field.name', 'python-super-package'),
42 # have a binary package release.51 # The summary is missing, since the source package doesn't
43 '&field.summary='52 # have a binary package release, and parse_qsl() excludes
44 '&field.title=Python+Super+Package')53 # empty params.
45 self.assertEqual(expected_url, url)54 ('field.source_package_name', 'python-super-package'),
55 ('field.title', 'Python Super Package'),
56 ]
57 base, query = urllib.splitquery(url)
58 params = cgi.parse_qsl(query)
59 self.assertEqual((expected_base, expected_params),
60 (base, params))
4661
47 def test_get_register_upstream_url_summary(self):62 def test_get_register_upstream_url_summary(self):
48 test_publisher = SoyuzTestPublisher()63 test_publisher = SoyuzTestPublisher()
@@ -57,15 +72,17 @@
57 # objects need to be reloaded.72 # objects need to be reloaded.
58 distroseries = getUtility(IDistroSeriesSet).get(distroseries_id)73 distroseries = getUtility(IDistroSeriesSet).get(distroseries_id)
59 source_package = distroseries.getSourcePackage(source_package_name)74 source_package = distroseries.getSourcePackage(source_package_name)
60 return_url = 'http://example.com/foo?a=b&c=d'75 url = get_register_upstream_url(source_package)
61 url = get_register_upstream_url(source_package, return_url)
62 expected_base = '/projects/+new'76 expected_base = '/projects/+new'
63 expected_params = [77 expected_params = [
64 ('_return_url', 'http://example.com/foo?a=b&c=d'),78 ('_return_url',
79 'http://launchpad.dev/youbuntu/busy/+source/bonkers'),
65 ('field.__visited_steps__', 'projectaddstep1'),80 ('field.__visited_steps__', 'projectaddstep1'),
66 ('field.actions.continue', 'Continue'),81 ('field.actions.continue', 'Continue'),
67 ('field.displayname', 'Bonkers'),82 ('field.displayname', 'Bonkers'),
83 ('field.distroseries', 'youbuntu/busy'),
68 ('field.name', 'bonkers'),84 ('field.name', 'bonkers'),
85 ('field.source_package_name', 'bonkers'),
69 ('field.summary', 'summary for flubber-bin\n'86 ('field.summary', 'summary for flubber-bin\n'
70 + 'summary for flubber-lib'),87 + 'summary for flubber-lib'),
71 ('field.title', 'Bonkers'),88 ('field.title', 'Bonkers'),
@@ -77,32 +94,47 @@
7794
78 def test_get_register_upstream_url_summary_duplicates(self):95 def test_get_register_upstream_url_summary_duplicates(self):
7996
80 class FakeDistroSeriesBinaryPackage:97 class Faker:
81 def __init__(self, summary):98 # Fakes attributes easily.
82 self.summary = summary99 def __init__(self, **kw):
83100 self.__dict__.update(kw)
84 class FakeDistributionSourcePackageRelease:101
85 sample_binary_packages = [102 class FakeSourcePackage(Faker):
86 FakeDistroSeriesBinaryPackage('summary for foo'),103 # Interface necessary for canonical_url() call in
87 FakeDistroSeriesBinaryPackage('summary for bar'),104 # get_register_upstream_url().
88 FakeDistroSeriesBinaryPackage('summary for baz'),105 implements(ISourcePackage)
89 FakeDistroSeriesBinaryPackage('summary for baz'),106
90 ]107 class FakeDistroSeries(Faker):
91108 implements(IDistroSeries)
92 class FakeSourcePackage:109
93 name = 'foo'110 class FakeDistribution(Faker):
94 releases = [FakeDistributionSourcePackageRelease()]111 implements(IDistribution)
95112
96 source_package = FakeSourcePackage()113 releases = Faker(sample_binary_packages=[
97 return_url = 'http://example.com/foo?a=b&c=d'114 Faker(summary='summary for foo'),
98 url = get_register_upstream_url(source_package, return_url)115 Faker(summary='summary for bar'),
116 Faker(summary='summary for baz'),
117 Faker(summary='summary for baz'),
118 ])
119 source_package = FakeSourcePackage(
120 name='foo',
121 sourcepackagename=Faker(name='foo'),
122 distroseries=FakeDistroSeries(
123 name='walrus',
124 distribution=FakeDistribution(name='zoobuntu')),
125 releases=[releases])
126
127 url = get_register_upstream_url(source_package)
99 expected_base = '/projects/+new'128 expected_base = '/projects/+new'
100 expected_params = [129 expected_params = [
101 ('_return_url', 'http://example.com/foo?a=b&c=d'),130 ('_return_url',
131 'http://launchpad.dev/zoobuntu/walrus/+source/foo'),
102 ('field.__visited_steps__', 'projectaddstep1'),132 ('field.__visited_steps__', 'projectaddstep1'),
103 ('field.actions.continue', 'Continue'),133 ('field.actions.continue', 'Continue'),
104 ('field.displayname', 'Foo'),134 ('field.displayname', 'Foo'),
135 ('field.distroseries', 'zoobuntu/walrus'),
105 ('field.name', 'foo'),136 ('field.name', 'foo'),
137 ('field.source_package_name', 'foo'),
106 ('field.summary', 'summary for bar\n'138 ('field.summary', 'summary for bar\n'
107 + 'summary for baz\n'139 + 'summary for baz\n'
108 + 'summary for foo'),140 + 'summary for foo'),
109141
=== modified file 'lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt'
--- lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt 2010-08-12 16:55:39 +0000
+++ lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt 2010-08-12 16:55:41 +0000
@@ -84,11 +84,13 @@
84 ... 'Register the upstream project').selected = True84 ... 'Register the upstream project').selected = True
85 >>> user_browser.getControl("Link to Upstream Project").click()85 >>> user_browser.getControl("Link to Upstream Project").click()
86 >>> print user_browser.url.replace('&', '\n&')86 >>> print user_browser.url.replace('&', '\n&')
87 http://launchpad.dev/projects/+new?_return_url=http...%2Bindex87 http://launchpad.dev/projects/+new?_return_url=http...%2Bsource%2Fbonkers
88 &field.__visited_steps__=projectaddstep188 &field.__visited_steps__=projectaddstep1
89 &field.actions.continue=Continue89 &field.actions.continue=Continue
90 &field.displayname=Bonkers90 &field.displayname=Bonkers
91 &field.distroseries=youbuntu%2Fbusy
91 &field.name=bonkers92 &field.name=bonkers
93 &field.source_package_name=bonkers
92 &field.summary=summary+for+flubber-bin%0Asummary+for+flubber-lib94 &field.summary=summary+for+flubber-bin%0Asummary+for+flubber-lib
93 &field.title=Bonkers95 &field.title=Bonkers
94 >>> print user_browser.getControl(name='field.name').value96 >>> print user_browser.getControl(name='field.name').value
@@ -118,11 +120,13 @@
118120
119 >>> user_browser.getLink("Register the upstream project").click()121 >>> user_browser.getLink("Register the upstream project").click()
120 >>> print user_browser.url.replace('&', '\n&')122 >>> print user_browser.url.replace('&', '\n&')
121 http://launchpad.dev/projects/+new?_return_url=http...%2Bedit-packaging123 http://launchpad.dev/projects/+new?_return_url=http...%2Bsource%2Fbonkers
122 &field.__visited_steps__=projectaddstep1124 &field.__visited_steps__=projectaddstep1
123 &field.actions.continue=Continue125 &field.actions.continue=Continue
124 &field.displayname=Bonkers126 &field.displayname=Bonkers
127 &field.distroseries=youbuntu%2Fbusy
125 &field.name=bonkers128 &field.name=bonkers
129 &field.source_package_name=bonkers
126 &field.summary=summary+for+flubber-bin%0Asummary+for+flubber-lib130 &field.summary=summary+for+flubber-bin%0Asummary+for+flubber-lib
127 &field.title=Bonkers131 &field.title=Bonkers
128 >>> print user_browser.getControl(name='field.name').value132 >>> print user_browser.getControl(name='field.name').value
@@ -140,9 +144,19 @@
140144
141If there are no problems with the prefilled data, then the license145If there are no problems with the prefilled data, then the license
142just needs to be selected. The user will then be redirected back146just needs to be selected. The user will then be redirected back
143to the source package page so that it can be linked.147to the source package page and an informational message will be displayed.
144148
145 >>> user_browser.getControl(name='field.licenses').value = ['BSD']149 >>> user_browser.getControl(name='field.licenses').value = ['BSD']
146 >>> user_browser.getControl("Complete Registration").click()150 >>> user_browser.getControl(
151 ... "Complete registration and link to bonkers package").click()
147 >>> print user_browser.url152 >>> print user_browser.url
148 http://launchpad.dev/youbuntu/busy/+source/bonkers/+edit-packaging153 http://launchpad.dev/youbuntu/busy/+source/bonkers
154 >>> for tag in find_tags_by_class(
155 ... user_browser.contents, 'informational message'):
156 ... print extract_text(tag)
157 Linked Bonkers project to bonkers source package.
158 >>> print extract_text(
159 ... find_tag_by_id(user_browser.contents, 'upstreams'))
160 Bonkers &rArr; trunk
161 Change upstream link
162 Remove upstream link...
149163
=== 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-12 16:55:41 +0000
@@ -299,8 +299,8 @@
299 <img src="/@@/info" />299 <img src="/@@/info" />
300 There are similar projects already registered in Launchpad.300 There are similar projects already registered in Launchpad.
301 Is project301 Is project
302 <strong><tal:displayname tal:replace="view/request/displayname" />302 <strong><tal:displayname tal:replace="view/request/field.displayname" />
303 (<tal:name tal:replace="view/request/name" />)</strong>303 (<tal:name tal:replace="view/request/field.name" />)</strong>
304 one of these?304 one of these?
305 </div>305 </div>
306306
@@ -326,8 +326,8 @@
326 tal:condition="view/search_results_count"326 tal:condition="view/search_results_count"
327 >Registration details</h3>327 >Registration details</h3>
328 Select the licenses for project328 Select the licenses for project
329 <strong><tal:displayname tal:replace="view/request/displayname" />329 <strong><tal:displayname tal:replace="view/request/field.displayname" />
330 (<tal:name tal:replace="view/request/name" />)</strong>330 (<tal:name tal:replace="view/request/field.name" />)</strong>
331 and complete the registration. You may also update the project's331 and complete the registration. You may also update the project's
332 title and summary.332 title and summary.
333 </div>333 </div>

Subscribers

People subscribed via source and target branches

to status/vote changes: