Merge lp:~edwin-grubbs/launchpad/bug-525956-unlink-button into lp:launchpad

Proposed by Edwin Grubbs
Status: Merged
Approved by: Curtis Hovey
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~edwin-grubbs/launchpad/bug-525956-unlink-button
Merge into: lp:launchpad
Diff against target: 1398 lines (+373/-417)
26 files modified
lib/canonical/launchpad/icing/style-3-0.css.in (+1/-1)
lib/canonical/launchpad/templates/launchpad-form.pt (+7/-0)
lib/canonical/launchpad/webapp/launchpadform.py (+31/-3)
lib/lp/registry/browser/configure.zcml (+16/-0)
lib/lp/registry/browser/distributionsourcepackage.py (+1/-22)
lib/lp/registry/browser/packaging.py (+0/-96)
lib/lp/registry/browser/product.py (+11/-48)
lib/lp/registry/browser/sourcepackage.py (+48/-15)
lib/lp/registry/browser/tests/packaging-views.txt (+41/-46)
lib/lp/registry/browser/tests/sourcepackage-views.txt (+52/-6)
lib/lp/registry/browser/tests/test_packaging.py (+4/-2)
lib/lp/registry/model/productseries.py (+4/-2)
lib/lp/registry/model/sourcepackage.py (+5/-4)
lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt (+5/-4)
lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt (+9/-14)
lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt (+10/-6)
lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt (+1/-3)
lib/lp/registry/stories/product/xx-product-package-pages.txt (+11/-8)
lib/lp/registry/stories/productseries/xx-productseries-delete.txt (+4/-2)
lib/lp/registry/templates/distributionsourcepackage-index.pt (+3/-29)
lib/lp/registry/templates/distroseries-packaging.pt (+2/-37)
lib/lp/registry/templates/product-packages.pt (+8/-19)
lib/lp/registry/templates/sourcepackage-portlet-associations.pt (+1/-47)
lib/lp/registry/templates/sourcepackage-remove-packaging.pt (+34/-0)
lib/lp/registry/templates/sourcepackage-upstream-connections.pt (+63/-0)
lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt (+1/-3)
To merge this branch: bzr merge lp:~edwin-grubbs/launchpad/bug-525956-unlink-button
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code + ui Approve
Eleanor Berger (community) code ui* Approve
Review via email: mp+20334@code.launchpad.net

Commit message

Added ability to unlink sourcepackages and project series from the sourcepackage index page.

To post a comment you must log in.
Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :

Summary
-------

The sourcepackage index page had an edit button but no way to remove
a link to the upstream via the project series.

The $distroseries/+packaging page also displayed identical info,
so I moved that into a template that could be shared.

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

Moved identical formatting of upstream connection info from
distroseries-packaging.pt and sourcepackage-portlet-associations.pt
into sourcepackage-upstream-connections.pt. Added the (-) removal link
and moved the edit link next to the projectgroup/project/series info.
    lib/lp/registry/browser/configure.zcml
    lib/lp/registry/templates/distroseries-packaging.pt
    lib/lp/registry/templates/sourcepackage-portlet-associations.pt
    lib/lp/registry/templates/sourcepackage-upstream-connections.pt

Added $sourcepackage/+remove-packaging and $sourcepackage/upstream-connections.
    lib/lp/registry/browser/sourcepackage.py
    lib/lp/registry/browser/tests/sourcepackage-views.txt
    lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt
    lib/lp/registry/templates/sourcepackage-remove-packaging.pt

Drive-by improvement of formatting.
    lib/lp/registry/model/productseries.py
    lib/lp/registry/model/sourcepackage.py

Tests
-----

./bin/test -vv -t 'sourcepackage-views.txt|xx-show-distroseries-packaging.txt'

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

* Open https://launchpad.dev/ubuntu/hoary/+source/evolution
    * If there is a "Link to Upstream Project" button, use that
      since the removal button won't show up without an existing link.
    * The Upstream Connections portlet should show:
        GNOME ⇒ Evolution ⇒ trunk (E) (-)
      where (E) is the edit button, and (-) is the remove button.
    * Click on the (-) button.
    * Click on the Unlink button.
    * The Upstream Connections portlet should now show the
      "Link to Upstream Project" button again.
* Open https://launchpad.dev/ubuntu/hoary/+packaging
    * The table should now show the projectgroup/project/series like so:
        GNOME ⇒ Evolution ⇒ trunk (E) (-)

Revision history for this message
Eleanor Berger (intellectronica) :
review: Approve (code ui*)
Revision history for this message
Curtis Hovey (sinzui) wrote :

Hi Edwin.

This this was a nice feature to use on the sp page. I think we should consider using it on the dsp page: https://launchpad.dev/ubuntu/+source/evolution. We have a post back that some people have expressed concern about how easy it is to make a mistake. I would not say this is change we must do because the post back always returns the user to the dsp page--using the sp as the next_url would be confusing.

I think we should remove the edit/remove links from the +packaging listing. There is not enough information on this page to make that decision.

review: Needs Information (ui)
Revision history for this message
Edwin Grubbs (edwin-grubbs) wrote :
Download full text (47.0 KiB)

> Hi Edwin.
>
> This this was a nice feature to use on the sp page. I think we should consider
> using it on the dsp page: https://launchpad.dev/ubuntu/+source/evolution. We
> have a post back that some people have expressed concern about how easy it is
> to make a mistake. I would not say this is change we must do because the post
> back always returns the user to the dsp page--using the sp as the next_url
> would be confusing.
>
> I think we should remove the edit/remove links from the +packaging listing.
> There is not enough information on this page to make that decision.

This was supposed to be a simple change for the UI review. It has grown
quite large, so I can create a new merge proposal if you would like.

I started off trying to use +remove-packaging page for the
$distrosourcepackage/+packages page, but I saw that the replaced code
was also used by $product/+packages page, so I changed that page also.

After I got the +remove-packaging page to return to the previous page, I
discovered that the edit link on the $distrosourcepackage/+packages was
returning you to a different page.

All these changes broke a ton of tests.

Pages to check
--------------

The edit and remove buttons for each series are hidden now.
https://launchpad.dev/ubuntu/hoary/+packaging

The remove button now takes you to the $sourcepackage/+remove-packaging page,
and the edit button no
https://launchpad.dev/ubuntu/+source/evolution

The remove button now takes you to the $sourcepackage/+remove-packaging page.
https://launchpad.dev/evolution/+packages

This page shouldn't have changed, but if you want to check that the
show_edit_buttons annotation is passed correctly, you'll need to look at it.
https://launchpad.dev/ubuntu/hoary/+source/evolution

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

Removed trailing white-space.
    lib/canonical/launchpad/icing/style-3-0.css.in

Added Mixin to enable forms to return to their referring page.
    lib/canonical/launchpad/templates/launchpad-form.pt
    lib/canonical/launchpad/webapp/launchpadform.py

Use the ReturnToReferrerMixin that provides the cancel_url and next_url.
Hide the edit/remove buttons by default, but show them in the
annotations portlet, because its view sets
request.annotations['show_edit_buttons'].
sourcepackage-upstream-connections.pt now has it's own view, since
it can't share the view with annotations portlet or show_edit_buttons
would always be true.
    lib/lp/registry/templates/sourcepackage-upstream-connections.pt
    lib/lp/registry/browser/sourcepackage.py
    lib/lp/registry/browser/tests/packaging-views.txt
    lib/lp/registry/browser/configure.zcml

Removed PackagingDeleteView and related code.
    lib/lp/registry/browser/distributionsourcepackage.py
    lib/lp/registry/browser/packaging.py
    lib/lp/registry/browser/product.py
    lib/lp/registry/templates/distributionsourcepackage-index.pt
    lib/lp/registry/templates/product-packages.pt

Added error message for when a user loads this page after the
upstream link has been deleted by someone else.
    lib/lp/registry/templates/sourcepackage-remove-packaging.pt

Tests:
    lib/lp/registry/browser/tests/sourcepackage-views.txt
    lib/lp/...

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

Hi Edwin.

in retrospect, saying no to updating the DSP page would have been better. On the other hand, I and Adi have wanted a mixin as you have created for sometime. I think the pain was worth it, though it was untimely.

I have one question from the diff. What is "irumpy hotter" in lp/registry/browser/tests/packaging-views.txt? This looks like a paste error. I am not aware of a irumpty test distroseries in the test

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

> I have one question from the diff. What is "irumpy hotter" in
> lp/registry/browser/tests/packaging-views.txt? This looks like a paste error.
> I am not aware of a irumpty test distroseries in the test

Doh. "irumpy" is so nonsensical and undesirable that irumpy.com is available. Fixed.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/icing/style-3-0.css.in'
2--- lib/canonical/launchpad/icing/style-3-0.css.in 2010-02-17 00:43:19 +0000
3+++ lib/canonical/launchpad/icing/style-3-0.css.in 2010-03-06 06:27:33 +0000
4@@ -814,7 +814,7 @@
5 }
6 input[type="submit"].icon-only {
7 vertical-align: middle;
8- border: 0;
9+ border: 0;
10 padding: 0;
11 height: 16px;
12 width: 16px;
13
14=== modified file 'lib/canonical/launchpad/templates/launchpad-form.pt'
15--- lib/canonical/launchpad/templates/launchpad-form.pt 2009-11-26 09:18:22 +0000
16+++ lib/canonical/launchpad/templates/launchpad-form.pt 2010-03-06 06:27:33 +0000
17@@ -16,6 +16,13 @@
18 accept-charset="UTF-8">
19
20 <div metal:define-macro="formbody">
21+ <tal:comment condition="nothing">
22+ This field is used by the ReturnToReferrerMixin.
23+ </tal:comment>
24+ <input type="hidden"
25+ name="_return_url"
26+ tal:condition="view/_return_url | nothing"
27+ tal:attributes="value view/_return_url" />
28
29 <p metal:define-slot="extra_info" tal:replace="nothing">
30 This is the description of the form.
31
32=== modified file 'lib/canonical/launchpad/webapp/launchpadform.py'
33--- lib/canonical/launchpad/webapp/launchpadform.py 2010-02-19 01:50:36 +0000
34+++ lib/canonical/launchpad/webapp/launchpadform.py 2010-03-06 06:27:33 +0000
35@@ -7,10 +7,11 @@
36 __metaclass__ = type
37
38 __all__ = [
39- 'LaunchpadFormView',
40- 'LaunchpadEditFormView',
41 'action',
42 'custom_widget',
43+ 'LaunchpadEditFormView',
44+ 'LaunchpadFormView',
45+ 'ReturnToReferrerMixin',
46 'safe_action',
47 ]
48
49@@ -32,7 +33,7 @@
50 IMultiLineWidgetLayout, ICheckBoxWidgetLayout,
51 IAlwaysSubmittedWidget, UnsafeFormGetSubmissionError)
52 from canonical.launchpad.webapp.menu import escape
53-from canonical.launchpad.webapp.publisher import LaunchpadView
54+from canonical.launchpad.webapp.publisher import canonical_url, LaunchpadView
55
56
57 classImplements(CheckBoxWidget, ICheckBoxWidgetLayout)
58@@ -434,3 +435,30 @@
59 """
60 action.is_safe = True
61 return action
62+
63+
64+class ReturnToReferrerMixin:
65+ """Return to the previous page after submitting the form.
66+
67+ The _return_url is stored in a hidden field in the launchpad-form.pt
68+ between the request to view the form and submitting the form.
69+ """
70+
71+ @property
72+ def _return_url(self):
73+ """See `LaunchpadFormView`."""
74+ # The referer header we want is only available before the view's
75+ # form submits to itself. This field is a hidden input in the form.
76+ referrer = self.request.form.get('_return_url')
77+ if referrer is None:
78+ # "referer" is misspelled in the HTTP specification.
79+ referrer = self.request.getHeader('referer')
80+
81+ if (referrer is not None
82+ and referrer.startswith(self.request.getApplicationURL())):
83+ return referrer
84+ else:
85+ return canonical_url(self.context)
86+
87+ next_url = _return_url
88+ cancel_url = _return_url
89
90=== modified file 'lib/lp/registry/browser/configure.zcml'
91--- lib/lp/registry/browser/configure.zcml 2010-03-05 14:18:18 +0000
92+++ lib/lp/registry/browser/configure.zcml 2010-03-06 06:27:33 +0000
93@@ -1946,6 +1946,14 @@
94 facet="overview"
95 class="lp.registry.browser.sourcepackage.SourcePackageAssociationPortletView"
96 template="../templates/sourcepackage-portlet-associations.pt"/>
97+
98+ <browser:page
99+ for="lp.registry.interfaces.sourcepackage.ISourcePackage"
100+ permission="zope.Public"
101+ name="+upstream-connections"
102+ facet="overview"
103+ class="lp.registry.browser.sourcepackage.SourcePackageUpstreamConnectionsView"
104+ template="../templates/sourcepackage-upstream-connections.pt"/>
105 <browser:page
106 for="lp.registry.interfaces.sourcepackage.ISourcePackage"
107 permission="zope.Public"
108@@ -2122,6 +2130,14 @@
109 facet="overview"
110 template="../templates/sourcepackage-edit-packaging.pt"
111 />
112+ <browser:page
113+ for="lp.registry.interfaces.sourcepackage.ISourcePackage"
114+ permission="launchpad.AnyPerson"
115+ class="lp.registry.browser.sourcepackage.SourcePackageRemoveUpstreamView"
116+ name="+remove-packaging"
117+ facet="overview"
118+ template="../templates/sourcepackage-remove-packaging.pt"
119+ />
120
121 <browser:page
122 for="lp.registry.interfaces.structuralsubscription.IStructuralSubscriptionTarget"
123
124=== modified file 'lib/lp/registry/browser/distributionsourcepackage.py'
125--- lib/lp/registry/browser/distributionsourcepackage.py 2010-01-06 12:02:38 +0000
126+++ lib/lp/registry/browser/distributionsourcepackage.py 2010-03-06 06:27:33 +0000
127@@ -49,7 +49,6 @@
128 from lp.soyuz.interfaces.distributionsourcepackagerelease import (
129 IDistributionSourcePackageRelease)
130 from lp.soyuz.interfaces.packagediff import IPackageDiffSet
131-from lp.registry.browser.packaging import PackagingDeleteView
132 from lp.registry.interfaces.pocket import pocketsuffix
133 from lp.translations.browser.customlanguagecode import (
134 HasCustomLanguageCodesTraversalMixin)
135@@ -243,7 +242,7 @@
136
137
138 class DistributionSourcePackageView(DistributionSourcePackageBaseView,
139- PackagingDeleteView):
140+ LaunchpadView):
141 """View class for DistributionSourcePackage."""
142 implements(IDistributionSourcePackageActionMenu)
143
144@@ -257,14 +256,6 @@
145 return canonical_url(self.context)
146
147 @property
148- def all_packaging(self):
149- """See `PackagingDeleteView`."""
150- for sourcepackage in self.context.get_distroseries_packages():
151- packaging = sourcepackage.direct_packaging
152- if packaging is not None:
153- yield packaging
154-
155- @property
156 def all_published_in_active_distroseries(self):
157 """Return a list of publishings in each active distroseries.
158
159@@ -408,16 +399,6 @@
160 for distroseries in self.active_series:
161 # The first row for each series is the "title" row.
162 packaging = packages_by_series[distroseries].direct_packaging
163- if packaging is None:
164- delete_packaging_form_id = None
165- hidden_packaging_field = None
166- else:
167- delete_packaging_form_id = "delete_%s_%s_%s" % (
168- packaging.distroseries.name,
169- packaging.productseries.product.name,
170- packaging.productseries.name)
171- hidden_packaging_field = self._renderHiddenPackagingField(
172- packaging)
173 package = packages_by_series[distroseries]
174 title_row = {
175 'blank_row': False,
176@@ -426,8 +407,6 @@
177 'distroseries': distroseries,
178 'series_package': package,
179 'packaging': packaging,
180- 'hidden_packaging_field': hidden_packaging_field,
181- 'delete_packaging_form_id': delete_packaging_form_id,
182 }
183 rows.append(title_row)
184
185
186=== removed file 'lib/lp/registry/browser/packaging.py'
187--- lib/lp/registry/browser/packaging.py 2010-01-21 23:46:24 +0000
188+++ lib/lp/registry/browser/packaging.py 1970-01-01 00:00:00 +0000
189@@ -1,96 +0,0 @@
190-# Copyright 2009 Canonical Ltd. This software is licensed under the
191-# GNU Affero General Public License version 3 (see the file LICENSE).
192-
193-__metaclass__ = type
194-
195-__all__ = [
196- 'PackagingDeleteView',
197- ]
198-
199-from zope.component import getUtility
200-from zope.formlib import form
201-from zope.schema import Choice
202-from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
203-
204-from canonical.launchpad import _
205-from lp.registry.interfaces.packaging import IPackagingUtil
206-from canonical.launchpad.webapp.launchpadform import action, LaunchpadFormView
207-
208-
209-class PackagingDeleteView(LaunchpadFormView):
210- """A base view that provides packaging link deletion."""
211-
212- @property
213- def all_packaging(self):
214- """An iterator of the context's packaging links."""
215- raise NotImplementedError
216-
217- def setUpFields(self):
218- """See `LaunchpadFormView`."""
219- # No schema is set in this form, because all fields are created with
220- # custom vocabularies. So we must not call the inherited setUpField
221- # method.
222- self.form_fields = self._createPackagingField()
223-
224- @property
225- def can_delete_packaging(self):
226- """Whether the user can delete existing packaging links."""
227- return self.user is not None
228-
229- def _createPackagingField(self):
230- """Create a field to specify a Packaging association.
231-
232- Create a contextual vocabulary that can specify one of the Packaging
233- associated to this DistributionSourcePackage.
234- """
235- terms = []
236- for packaging in self.all_packaging:
237- terms.append(SimpleTerm(packaging, packaging.id))
238- return form.Fields(
239- Choice(__name__='packaging', vocabulary=SimpleVocabulary(terms),
240- required=True))
241-
242- def _renderHiddenPackagingField(self, packaging):
243- """Render a hidden input that fills in the packaging field."""
244- if not self.can_delete_packaging:
245- return None
246- vocabulary = self.form_fields['packaging'].field.vocabulary
247- return '<input type="hidden" name="field.packaging" value="%s" />' % (
248- vocabulary.getTerm(packaging).token)
249-
250- def renderDeletePackagingAction(self):
251- """Render a submit input for the delete_packaging_action."""
252- assert self.can_delete_packaging, 'User cannot delete Packaging.'
253- return ('<input type="image" value="Delete Link" '
254- 'src="/@@/remove" title="Delete upsteam link" '
255- 'name="%s"/>' % self.delete_packaging_action.__name__)
256-
257- def handleDeletePackagingError(self, action, data, errors):
258- """Handle errors on package link deletion.
259-
260- If 'packaging' is not set in the form data, we assume that means the
261- provided Packaging id was not found, which should only happen if the
262- same Packaging object was concurrently deleted. In this case, we want
263- to display a more informative error message than the default 'Invalid
264- value'.
265- """
266- if data.get('packaging') is None:
267- self.setFieldError(
268- 'packaging',
269- _("This upstream association was deleted already."))
270-
271- @action(_("Delete Link"), name='delete_packaging',
272- failure=handleDeletePackagingError)
273- def delete_packaging_action(self, action, data):
274- """Delete a Packaging association."""
275- packaging = data['packaging']
276- productseries = packaging.productseries
277- distroseries = packaging.distroseries
278- getUtility(IPackagingUtil).deletePackaging(
279- productseries, packaging.sourcepackagename, distroseries)
280- self.request.response.addNotification(
281- _("Removed upstream association between ${product} "
282- "${productseries} and ${distroseries}.", mapping=dict(
283- product=productseries.product.displayname,
284- productseries=productseries.displayname,
285- distroseries=distroseries.displayname)))
286
287=== modified file 'lib/lp/registry/browser/product.py'
288--- lib/lp/registry/browser/product.py 2010-02-16 21:21:14 +0000
289+++ lib/lp/registry/browser/product.py 2010-03-06 06:27:33 +0000
290@@ -81,7 +81,6 @@
291 from lp.registry.browser.distribution import UsesLaunchpadMixin
292 from lp.registry.browser.menu import (
293 IRegistryCollectionNavigationMenu, RegistryCollectionActionMenuBase)
294-from lp.registry.browser.packaging import PackagingDeleteView
295 from lp.answers.browser.faqtarget import FAQTargetNavigationMixin
296 from canonical.launchpad.browser.feeds import FeedsMixin
297 from lp.registry.browser.productseries import get_series_branch_error
298@@ -95,13 +94,15 @@
299 StructuralSubscriptionTargetTraversalMixin)
300 from canonical.launchpad.mail import format_address, simple_sendmail
301 from canonical.launchpad.webapp import (
302- ApplicationMenu, LaunchpadEditFormView, LaunchpadFormView, LaunchpadView,
303- Link, Navigation, StandardLaunchpadFacets, action, canonical_url,
304- custom_widget, enabled_with_permission, sorted_version_numbers,
305+ ApplicationMenu, canonical_url, enabled_with_permission, LaunchpadView,
306+ Link, Navigation, sorted_version_numbers, StandardLaunchpadFacets,
307 stepthrough, stepto, structured)
308 from canonical.launchpad.webapp.authorization import check_permission
309 from canonical.launchpad.webapp.batching import BatchNavigator
310 from canonical.launchpad.webapp.breadcrumb import Breadcrumb
311+from canonical.launchpad.webapp.launchpadform import (
312+ action, custom_widget, LaunchpadEditFormView, LaunchpadFormView,
313+ ReturnToReferrerMixin)
314 from canonical.launchpad.webapp.menu import NavigationMenu
315 from canonical.widgets.popup import PersonPickerWidget
316 from canonical.widgets.date import DateWidget
317@@ -897,17 +898,11 @@
318 check_permission('launchpad.Commercial', self.context))
319
320
321-class ProductPackagesView(PackagingDeleteView):
322+class ProductPackagesView(LaunchpadView):
323 """View for displaying product packaging"""
324
325 label = 'Linked packages'
326-
327- @property
328- def all_packaging(self):
329- """See `PackagingDeleteView`."""
330- for series in self.context.series:
331- for packaging in series.packagings:
332- yield packaging
333+ page_title = label
334
335 @cachedproperty
336 def series_packages(self):
337@@ -921,23 +916,11 @@
338 field: '<input type=''hidden' ...>},
339 }]
340 """
341- # This method is a superset of all_packaging. While all_packaging will
342- # be called several times as data is mutated, series_packages should
343- # only be called during render().
344 packaged_series = []
345 for series in self.context.series:
346 packagings = []
347 for packaging in series.packagings:
348- form_id = 'delete-%s-%s-%s' % (
349- packaging.distroseries.name,
350- packaging.sourcepackagename.name,
351- packaging.productseries.name,
352- )
353- packaging_field = dict(
354- packaging=packaging,
355- form_id=form_id,
356- field=self._renderHiddenPackagingField(packaging))
357- packagings.append(packaging_field)
358+ packagings.append(packaging)
359 packaged_series.append(dict(
360 series=series, packagings=packagings))
361 return packaged_series
362@@ -1262,7 +1245,8 @@
363 return canonical_url(self.context)
364
365
366-class ProductReviewLicenseView(ProductEditView, EditPrivateBugsMixin):
367+class ProductReviewLicenseView(ReturnToReferrerMixin,
368+ ProductEditView, EditPrivateBugsMixin):
369 """A view to review a project and change project privileges."""
370 label = "Review project"
371 field_names = [
372@@ -1302,27 +1286,6 @@
373 # supervisor.
374 self.validate_private_bugs(data)
375
376- @property
377- def next_url(self):
378- """See `LaunchpadFormView`."""
379- # The referer header we want is only available before the view's
380- # form submits to itself. This field is a hidden input in the form.
381- referrer = self.request.form.get('next_url')
382- if referrer is None:
383- referrer = self.request.getHeader('referer')
384-
385- if (referrer is not None
386- and referrer.startswith(self.request.getApplicationURL())):
387- return referrer
388- else:
389- return canonical_url(self.context)
390-
391- @property
392- def cancel_url(self):
393- """See `LaunchpadFormView`."""
394- return self.next_url
395-
396-
397 class ProductAddSeriesView(LaunchpadFormView):
398 """A form to add new product series"""
399
400@@ -1613,7 +1576,7 @@
401 self.request.form['summary'] = data['summary']
402
403
404-class ProjectAddStepTwo(StepView, ProductLicenseMixin):
405+class ProjectAddStepTwo(StepView, ProductLicenseMixin, ReturnToReferrerMixin):
406 """Step 2 (of 2) in the +new project add wizard."""
407
408 _field_names = ['displayname', 'name', 'title', 'summary',
409
410=== modified file 'lib/lp/registry/browser/sourcepackage.py'
411--- lib/lp/registry/browser/sourcepackage.py 2010-02-23 19:43:58 +0000
412+++ lib/lp/registry/browser/sourcepackage.py 2010-03-06 06:27:33 +0000
413@@ -12,7 +12,8 @@
414 'SourcePackageFacets',
415 'SourcePackageHelpView',
416 'SourcePackageNavigation',
417- 'SourcePackagePackaging',
418+ 'SourcePackageRemoveUpstreamView',
419+ 'SourcePackageUpstreamConnectionsView',
420 'SourcePackageView',
421 ]
422
423@@ -40,7 +41,7 @@
424 from lp.answers.browser.questiontarget import (
425 QuestionTargetFacetMixin, QuestionTargetAnswersMenu)
426 from lp.services.worlddata.interfaces.country import ICountry
427-from lp.registry.interfaces.packaging import IPackaging
428+from lp.registry.interfaces.packaging import IPackaging, IPackagingUtil
429 from lp.registry.interfaces.pocket import PackagePublishingPocket
430 from lp.registry.interfaces.product import IProductSet
431 from lp.registry.interfaces.productseries import IProductSeries
432@@ -49,12 +50,15 @@
433 from lp.translations.interfaces.potemplate import IPOTemplateSet
434 from canonical.launchpad import _
435 from canonical.launchpad.webapp import (
436- action, ApplicationMenu, custom_widget, GetitemNavigation,
437- LaunchpadFormView, Link, redirection, StandardLaunchpadFacets, stepto)
438+ ApplicationMenu, GetitemNavigation, Link, redirection,
439+ StandardLaunchpadFacets, stepto)
440+from canonical.launchpad.webapp.launchpadform import (
441+ action, custom_widget, LaunchpadFormView, ReturnToReferrerMixin)
442 from canonical.launchpad.webapp import canonical_url
443 from canonical.launchpad.webapp.authorization import check_permission
444 from canonical.launchpad.webapp.breadcrumb import Breadcrumb
445 from canonical.launchpad.webapp.menu import structured
446+from canonical.launchpad.webapp.publisher import LaunchpadView
447
448 from canonical.lazr.utils import smartquote
449
450@@ -108,7 +112,7 @@
451 usedfor = ISourcePackage
452 facet = 'overview'
453 links = [
454- 'distribution_source_package', 'edit_packaging',
455+ 'distribution_source_package', 'edit_packaging', 'remove_packaging',
456 'changelog', 'builds', 'set_upstream',
457 ]
458
459@@ -124,6 +128,10 @@
460 def edit_packaging(self):
461 return Link('+edit-packaging', 'Change upstream link', icon='edit')
462
463+ def remove_packaging(self):
464+ return Link(
465+ '+remove-packaging', 'Remove upstream link', icon='remove')
466+
467 def set_upstream(self):
468 return Link("+edit-packaging", "Set upstream link", icon="add")
469
470@@ -143,7 +151,7 @@
471 return Link('+gethelp', 'Help and support options', icon='info')
472
473
474-class SourcePackageChangeUpstreamStepOne(StepView):
475+class SourcePackageChangeUpstreamStepOne(ReturnToReferrerMixin, StepView):
476 """A view to set the `IProductSeries` of a sourcepackage."""
477 schema = Interface
478 _field_names = []
479@@ -167,9 +175,8 @@
480 IProductSeries['product'], default=default)
481 self.form_fields += Fields(product_field)
482
483- @property
484- def cancel_url(self):
485- return canonical_url(self.context)
486+ # Override ReturnToReferrerMixin.next_url.
487+ next_url = None
488
489 def main_action(self, data):
490 """See `MultiStepView`."""
491@@ -177,7 +184,7 @@
492 self.request.form['product'] = data['product']
493
494
495-class SourcePackageChangeUpstreamStepTwo(StepView):
496+class SourcePackageChangeUpstreamStepTwo(ReturnToReferrerMixin, StepView):
497 """A view to set the `IProductSeries` of a sourcepackage."""
498 schema = IProductSeries
499 _field_names = ['product']
500@@ -196,10 +203,6 @@
501 custom_widget('product', DropdownWidget, visible=False)
502 custom_widget('productseries', LaunchpadRadioWidget)
503
504- @property
505- def cancel_url(self):
506- return canonical_url(self.context)
507-
508 def setUpFields(self):
509 super(SourcePackageChangeUpstreamStepTwo, self).setUpFields()
510
511@@ -261,12 +264,16 @@
512 Fields(display_product_field, productseries_choice)
513 + self.form_fields)
514
515+ # Override ReturnToReferrerMixin.next_url until the main_action()
516+ # is called.
517+ next_url = None
518+
519 main_action_label = u'Change'
520 def main_action(self, data):
521 productseries = data['productseries']
522 # Because it is part of a multistep view, the next_url can't
523 # be set until the action is called, or it will skip the step.
524- self.next_url = canonical_url(self.context)
525+ self.next_url = self._return_url
526 if self.context.productseries == productseries:
527 # There is nothing to do.
528 return
529@@ -282,6 +289,27 @@
530 first_step = SourcePackageChangeUpstreamStepOne
531
532
533+class SourcePackageRemoveUpstreamView(ReturnToReferrerMixin,
534+ LaunchpadFormView):
535+ """A view for removing the link to an upstream package."""
536+
537+ schema = Interface
538+ field_names = []
539+ label = 'Unlink an upstream project'
540+ page_title = label
541+
542+ @action('Unlink')
543+ def unlink(self, action, data):
544+ old_series = self.context.productseries
545+ getUtility(IPackagingUtil).deletePackaging(
546+ self.context.productseries,
547+ self.context.sourcepackagename,
548+ self.context.distroseries)
549+ self.request.response.addInfoNotification(
550+ 'Removed upstream association between %s and %s.' % (
551+ old_series.title, self.context.distroseries.displayname))
552+
553+
554 class SourcePackageView:
555 """A view for (distro series) source packages."""
556
557@@ -411,6 +439,7 @@
558 def setUpFields(self):
559 """See `LaunchpadFormView`."""
560 super(SourcePackageAssociationPortletView, self).setUpFields()
561+ self.request.annotations['show_edit_buttons'] = True
562 # Find registered products that are similarly named to the source
563 # package.
564 product_vocab = getVocabularyRegistry().get(None, 'Product')
565@@ -444,6 +473,10 @@
566 upstream.displayname)
567 self.next_url = self.request.getURL()
568
569+
570+class SourcePackageUpstreamConnectionsView(LaunchpadView):
571+ """A shared view with upstream connection info."""
572+
573 @property
574 def has_bugtracker(self):
575 """Does the product have a bugtracker set?"""
576
577=== modified file 'lib/lp/registry/browser/tests/packaging-views.txt'
578--- lib/lp/registry/browser/tests/packaging-views.txt 2010-02-18 18:58:26 +0000
579+++ lib/lp/registry/browser/tests/packaging-views.txt 2010-03-06 06:27:33 +0000
580@@ -273,32 +273,19 @@
581 >>> print view.label
582 Linked packages
583
584-The view defines the all_packages property used by the PackagingDeleteView
585-to create a vocabulary.
586-
587- >>> for package in view.all_packaging:
588- ... print package.distroseries.name, package.productseries.name
589- grumpy hotter
590- hoary hotter
591-
592 The view provides the series_packages property that returns a list of
593-dicts. Each dict as a series and a list of package dicts. The package dict
594-contains the package and field for form actions.
595+dicts. Each dict as a series and a list of packages.
596
597- >>> for series_dict in view.series_packages:
598- ... print series_dict['series'].name
599- ... for package_dict in series_dict['packagings']:
600- ... print package_dict['packaging'].distroseries.name
601- ... print package_dict['form_id']
602- ... print package_dict['field']
603+ >>> def print_packages(view):
604+ ... for series_dict in view.series_packages:
605+ ... print series_dict['series'].name
606+ ... for package in series_dict['packagings']:
607+ ... print package.distroseries.name
608+ >>> print_packages(view)
609 cold
610 hotter
611 grumpy
612- delete-grumpy-hot-hotter
613- <input type="hidden" name="field.packaging" .../>
614 hoary
615- delete-hoary-thunderbird-hotter
616- <input type="hidden" name="field.packaging" .../>
617 trunk
618
619 The view provides the distro_packaging property that is a list of
620@@ -306,40 +293,48 @@
621 sorted by distribution with Ubuntu first and the rest in alphabetic
622 order.
623
624- >>> view = create_initialized_view(product, name='+packages')
625 >>> for distro_dict in view.distro_packaging:
626 ... print distro_dict['distribution'].name
627 ubuntu
628
629-The +packages named view descends from PackagingDeleteView to provide remove
630-link actions for the product's linked packages.
631-
632- >>> from lp.registry.browser.packaging import PackagingDeleteView
633-
634- >>> isinstance(view, PackagingDeleteView)
635- True
636-
637 A packaging link can be deleted if the owner believes it is an error. The
638 package linked to hoary is wrong; thunderbird is the wrong sourcepackage.
639
640+ >>> from canonical.launchpad.testing.pages import find_tag_by_id
641+ >>> view = create_initialized_view(
642+ ... product, name='+packages', principal=a_user)
643+ >>> print_packages(view)
644+ cold
645+ hotter
646+ grumpy
647+ hoary
648+ trunk
649+
650+ # There are links to the +remove-packaging page.
651+ >>> table = find_tag_by_id(view.render(), 'packages-hotter')
652+ >>> for link in table.findAll('a'):
653+ ... if '+remove-packaging' in link['href']:
654+ ... print link['href']
655+ http://launchpad.dev/ubuntu/grumpy/+source/hot/+remove-packaging
656+ http://launchpad.dev/ubuntu/hoary/+source/thunderbird/+remove-packaging
657+
658+ >>> [hoary_package] = [
659+ ... package for series_dict in view.series_packages
660+ ... for package in series_dict['packagings']
661+ ... if package.distroseries.name == 'hoary']
662+ >>> form = {'field.actions.unlink': 'Unlink'}
663+ >>> unlink_view = create_initialized_view(
664+ ... hoary_package.sourcepackage, name='+remove-packaging', form=form)
665+ >>> unlink_view.errors
666+ []
667+
668+ # The view has to be reloaded since view.series_packages is cached.
669 >>> view = create_initialized_view(product, name='+packages')
670- >>> for package in view.all_packaging:
671- ... print package.distroseries.name, package.productseries.name
672- grumpy hotter
673- hoary hotter
674-
675- >>> [hoary_package] = [package for package in view.all_packaging
676- ... if package.distroseries.name == 'hoary']
677- >>> form = {
678- ... 'field.packaging': '%s' % hoary_package.id,
679- ... 'field.actions.delete_packaging': 'Delete upstream link',
680- ... }
681- >>> view = create_initialized_view(product, name='+packages', form=form)
682- >>> view.errors
683- []
684- >>> for package in view.all_packaging:
685- ... print package.distroseries.name, package.productseries.name
686- grumpy hotter
687+ >>> print_packages(view)
688+ cold
689+ hotter
690+ grumpy
691+ trunk
692
693
694 Distro series +packaging view
695
696=== modified file 'lib/lp/registry/browser/tests/sourcepackage-views.txt'
697--- lib/lp/registry/browser/tests/sourcepackage-views.txt 2010-02-18 20:39:01 +0000
698+++ lib/lp/registry/browser/tests/sourcepackage-views.txt 2010-03-06 06:27:33 +0000
699@@ -145,6 +145,7 @@
700 >>> print view.request.response.notifications
701 []
702
703+
704 Upstream associations portlet
705 -----------------------------
706
707@@ -163,12 +164,18 @@
708
709 >>> from canonical.launchpad.testing.pages import (
710 ... extract_text, find_tag_by_id)
711- >>> content = extract_text(find_tag_by_id(view.render(), 'upstreams'))
712- >>> print content
713- Bonkers project
714+ >>> content = find_tag_by_id(view.render(), 'upstreams')
715+ >>> for link in content.findAll('a'):
716+ ... print link['href']
717+ /bonkers
718+ /bonkers/crazy
719+ .../+source/bonkers/+edit-packaging
720+ .../+source/bonkers/+remove-packaging
721+
722+ >>> print extract_text(content)
723+ Bonkers...crazy...
724 Bug supervisor: no
725 Bug tracker: no
726- Bonkers crazy series
727 Branch: no
728
729 A new source project that is not linked to an upstream will result in
730@@ -211,6 +218,9 @@
731 Lernid Dev...
732
733
734+Upstream connections view
735+-------------------------
736+
737 The view includes a property for determining if the project has a bug
738 tracker, though the rules are somewhat complicated.
739
740@@ -226,7 +236,8 @@
741 >>> package = factory.makeSourcePackage(
742 ... sourcepackagename=sourcepackagename, distroseries=distroseries)
743
744- >>> view = create_initialized_view(package, name='+portlet-associations')
745+ >>> view = create_initialized_view(
746+ ... package, name='+upstream-connections')
747
748 >>> print package.productseries
749 None
750@@ -242,7 +253,8 @@
751 If a product is not part of a project group and its bug tracker is not
752 set then the view property is false.
753
754- >>> view = create_initialized_view(package, name='+portlet-associations')
755+ >>> view = create_initialized_view(
756+ ... package, name='+upstream-connections')
757
758 >>> print product.official_malone
759 False
760@@ -285,3 +297,37 @@
761 >>> project.bugtracker = bugtracker
762 >>> print view.has_bugtracker
763 True
764+
765+
766+Remove packaging view
767+---------------------
768+
769+This view allows removal of the packaging link from the sourcepackage
770+to the project series.
771+
772+ >>> view = create_initialized_view(package, name='+remove-packaging')
773+ >>> print view.label
774+ Unlink an upstream project
775+
776+ >>> print view.page_title
777+ Unlink an upstream project
778+
779+ >>> print view.cancel_url
780+ http://launchpad.dev/youbuntu/wonky/+source/stinkypackage
781+
782+ >>> form = {'field.actions.unlink': 'Unlink'}
783+ >>> view = create_initialized_view(
784+ ... package, name='+remove-packaging', form=form,
785+ ... principal=product.owner)
786+ >>> view.errors
787+ []
788+
789+ >>> for notification in view.request.response.notifications:
790+ ... print notification.message
791+ Removed upstream association between Stinky stinkyseries series and Wonky.
792+
793+ >>> view = create_initialized_view(package, name='+portlet-associations')
794+ >>> print extract_text(find_tag_by_id(view.render(), 'no-upstreams'))
795+ There are no projects registered in Launchpad that are a potential
796+ match for this source package. Can you help us find one?
797+ Set upstream link
798
799=== modified file 'lib/lp/registry/browser/tests/test_packaging.py'
800--- lib/lp/registry/browser/tests/test_packaging.py 2009-10-23 12:53:21 +0000
801+++ lib/lp/registry/browser/tests/test_packaging.py 2010-03-06 06:27:33 +0000
802@@ -55,8 +55,10 @@
803 # Delete the packaging
804 user_browser = self.user_browser
805 user_browser.open('http://launchpad.dev/ubuntu/+source/alsa-utils')
806- form = user_browser.getForm("delete_warty_alsa-utils_trunk")
807- form.getControl(name="field.actions.delete_packaging").click()
808+ link = user_browser.getLink(
809+ url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
810+ link.click()
811+ user_browser.getControl('Unlink').click()
812 # Check that the change was committed.
813 login('no-priv@canonical.com')
814 self.assertFalse(packaging_util.packagingEntryExists(
815
816=== modified file 'lib/lp/registry/model/productseries.py'
817--- lib/lp/registry/model/productseries.py 2010-02-25 21:37:02 +0000
818+++ lib/lp/registry/model/productseries.py 2010-03-06 06:27:33 +0000
819@@ -412,8 +412,10 @@
820
821 # ok, we didn't find a packaging record that matches, let's go ahead
822 # and create one
823- pkg = Packaging(distroseries=distroseries,
824- sourcepackagename=sourcepackagename, productseries=self,
825+ pkg = Packaging(
826+ distroseries=distroseries,
827+ sourcepackagename=sourcepackagename,
828+ productseries=self,
829 packaging=PackagingType.PRIME,
830 owner=owner)
831 pkg.sync() # convert UTC_NOW to actual datetime
832
833=== modified file 'lib/lp/registry/model/sourcepackage.py'
834--- lib/lp/registry/model/sourcepackage.py 2010-02-25 21:37:02 +0000
835+++ lib/lp/registry/model/sourcepackage.py 2010-03-06 06:27:33 +0000
836@@ -477,10 +477,11 @@
837 target.datecreated = UTC_NOW
838 else:
839 # ok, we need to create a new one
840- Packaging(distroseries=self.distroseries,
841- sourcepackagename=self.sourcepackagename,
842- productseries=productseries, owner=user,
843- packaging=PackagingType.PRIME)
844+ Packaging(
845+ distroseries=self.distroseries,
846+ sourcepackagename=self.sourcepackagename,
847+ productseries=productseries, owner=user,
848+ packaging=PackagingType.PRIME)
849 # and make sure this change is immediately available
850 flush_database_updates()
851
852
853=== modified file 'lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt'
854--- lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt 2010-02-25 16:36:02 +0000
855+++ lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt 2010-03-06 06:27:33 +0000
856@@ -17,12 +17,13 @@
857 Source Package Upstream Project Upstream Contributor Connections
858 netapplet
859 NetApplet ... The Novell Network Applet
860- NetApplet ... project: Bug supervisor: no Bug tracker: yes
861- NetApplet trunk series: Branch: no
862+ GNOME...NetApplet...trunk
863+ ...Bug supervisor: no Bug tracker: yes Branch: no
864 evolution
865 Evolution ... Evolution is an email client, addressbook ...
866- Evolution ... project: Bug supervisor: no Bug tracker: yes
867- Evolution trunk series: Branch: yes Translations: no
868+ GNOME...Evolution...trunk
869+ ...Bug supervisor: no Bug tracker: yes
870+ Branch: yes Translations: no
871
872 Any use can see that this page is related to the needs packaging report. It
873 is linked, but the link to this page is not enabled.
874
875=== modified file 'lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt'
876--- lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt 2009-10-23 12:53:21 +0000
877+++ lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt 2010-03-06 06:27:33 +0000
878@@ -16,33 +16,28 @@
879 Then the user click the "Delete Link" button in the first tab. The
880 deletion succeeds and the usual informational message is displayed.
881
882- >>> form = first_browser.getForm("delete_warty_alsa-utils_trunk")
883- >>> form.getControl(name="field.actions.delete_packaging").click()
884+ >>> link = first_browser.getLink(
885+ ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
886+ >>> link.click()
887+ >>> first_browser.getControl('Unlink').click()
888 >>> content = first_browser.contents
889 >>> for tag in find_tags_by_class(content, 'error'):
890 ... print extract_text(tag)
891 >>> for tag in find_tags_by_class(content, 'informational'):
892 ... print extract_text(tag)
893- Removed upstream association between alsa-utils trunk and Warty.
894+ Removed upstream association between alsa-utils trunk series and Warty.
895
896 A few minutes later, the user sees the same packaging association in the
897 second tab, and clicks the "Delete Link" button again.
898
899 The packaging object has been deleted already, so this action cannot
900-succeed. We yield a more useful error message than the mysterious
901-"Invalid value" displayed by default.
902-
903-XXX: We would like to display an informational message and no error
904-message, but it would be too much trouble for a corner case like this.
905-So we resort to just displaying a helpful error message.
906--- David Allouche 2007-12-07
907-
908- >>> form = second_browser.getForm("delete_warty_alsa-utils_trunk")
909- >>> form.getControl(name="field.actions.delete_packaging").click()
910+succeed.
911+
912+ >>> second_browser.getLink(
913+ ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging').click()
914 >>> content = second_browser.contents
915 >>> for tag in find_tags_by_class(content, 'informational'):
916 ... print extract_text(tag)
917 >>> for tag in find_tags_by_class(content, 'error'):
918 ... print extract_text(tag)
919- There is 1 error.
920 This upstream association was deleted already.
921
922=== modified file 'lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt'
923--- lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt 2009-10-23 12:53:21 +0000
924+++ lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt 2010-03-06 06:27:33 +0000
925@@ -15,6 +15,7 @@
926 The Hoary Hedgehog Release (active development) Set upstream link
927 1.0.9a-4ubuntu1 release (main) ... weeks ago
928 The Warty Warthog Release (current stable release) alsa-utils trunk series
929+ ...
930 1.0.8-1ubuntu1 release (main) ... weeks ago
931 1.0.9a-4 release (main) ... weeks ago
932
933@@ -26,9 +27,10 @@
934 packaging links.
935
936 >>> user_browser.open('http://launchpad.dev/ubuntu/+source/alsa-utils')
937- >>> form = user_browser.getForm("delete_warty_alsa-utils_trunk")
938- >>> print form.getControl(name="field.actions.delete_packaging")
939- <ImageControl name='field.actions.delete_packaging' type='image'>
940+ >>> link = user_browser.getLink(
941+ ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
942+ >>> print link
943+ <Link text='Remove upstream link'...
944
945 This button is not displayed to anonymous users.
946
947@@ -40,14 +42,16 @@
948
949 Clicking this button deletes the corresponding packaging association.
950
951- >>> form = user_browser.getForm("delete_warty_alsa-utils_trunk")
952- >>> form.getControl(name="field.actions.delete_packaging").click()
953+ >>> link = user_browser.getLink(
954+ ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
955+ >>> link.click()
956+ >>> user_browser.getControl('Unlink').click()
957 >>> content = user_browser.contents
958 >>> for tag in find_tags_by_class(content, 'error'):
959 ... print extract_text(tag)
960 >>> for tag in find_tags_by_class(content, 'informational'):
961 ... print extract_text(tag)
962- Removed upstream association between alsa-utils trunk and Warty.
963+ Removed upstream association between alsa-utils trunk series and Warty.
964 >>> print extract_text(find_tag_by_id(content, 'packages_list'))
965 The Hoary Hedgehog Release (active development) Set upstream link
966 1.0.9a-4ubuntu1 release (main) ... weeks ago
967
968=== modified file 'lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt'
969--- lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt 2010-02-18 20:41:01 +0000
970+++ lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt 2010-03-06 06:27:33 +0000
971@@ -29,11 +29,9 @@
972 >>> user_browser.getControl("Change").click()
973 >>> print extract_text(find_tag_by_id(
974 ... user_browser.contents, 'upstreams'))
975- Mozilla Thunderbird ... project
976- Project Group: the Mozilla Project
977+ the Mozilla Project...Mozilla Thunderbird...trunk...
978 Bug supervisor: no
979 Bug tracker: no
980- Mozilla Thunderbird trunk series
981 Branch: no
982 Translations: no
983
984
985=== modified file 'lib/lp/registry/stories/product/xx-product-package-pages.txt'
986--- lib/lp/registry/stories/product/xx-product-package-pages.txt 2010-01-12 22:34:51 +0000
987+++ lib/lp/registry/stories/product/xx-product-package-pages.txt 2010-03-06 06:27:33 +0000
988@@ -30,8 +30,8 @@
989 >>> print extract_text(find_tag_by_id(
990 ... evo_owner.contents, 'packages-trunk'))
991 Distribution Distribution series Source package Version
992- Ubuntu Warty (4.10) evolution
993- Ubuntu Hoary (5.04) evolution 1.0
994+ Ubuntu Warty (4.10) evolution Remove...
995+ Ubuntu Hoary (5.04) evolution 1.0 Remove...
996
997 >>> evo_owner.getLink(url='/ubuntu/hoary/+source/evolution') is not None
998 True
999@@ -54,16 +54,19 @@
1000
1001 Packaging links can be deleted if they were created in error.
1002
1003- >>> form = evo_owner.getForm("delete-warty-evolution-trunk")
1004- >>> form.getControl(name="field.actions.delete_packaging").click()
1005- >>> print evo_owner.title
1006- Linked packages ...
1007+ >>> evo_owner.getLink(
1008+ ... url='/ubuntu/warty/+source/evolution/+remove-packaging').click()
1009+ >>> print evo_owner.title
1010+ Unlink an upstream project...
1011+ >>> evo_owner.getControl('Unlink').click()
1012+ >>> print evo_owner.title
1013+ Linked packages...
1014
1015 >>> for message in get_feedback_messages(evo_owner.contents):
1016 ... print message
1017- Removed upstream association between Evolution trunk and Warty.
1018+ Removed upstream association between Evolution trunk series and Warty.
1019
1020 >>> print extract_text(find_tag_by_id(
1021 ... evo_owner.contents, 'packages-trunk'))
1022 Distribution Distribution series Source package Version
1023- Ubuntu Hoary (5.04) evolution 1.0
1024+ Ubuntu Hoary (5.04) evolution 1.0 Remove...
1025
1026=== modified file 'lib/lp/registry/stories/productseries/xx-productseries-delete.txt'
1027--- lib/lp/registry/stories/productseries/xx-productseries-delete.txt 2009-12-14 13:49:03 +0000
1028+++ lib/lp/registry/stories/productseries/xx-productseries-delete.txt 2010-03-06 06:27:33 +0000
1029@@ -46,8 +46,10 @@
1030 Mozilla Firefox in Launchpad
1031
1032 >>> owner_browser.getLink('All packages').click()
1033- >>> owner_browser.getControl(
1034- ... name='field.actions.delete_packaging', index=1).click()
1035+ >>> link = owner_browser.getLink(
1036+ ... url='/ubuntu/warty/+source/mozilla-firefox/+remove-packaging')
1037+ >>> link.click()
1038+ >>> owner_browser.getControl('Unlink').click()
1039
1040 Then he returns to delete trunk. He is informed that deletion is permanent,
1041 and that the milestones, releases, and files will also be deleted. The
1042
1043=== modified file 'lib/lp/registry/templates/distributionsourcepackage-index.pt'
1044--- lib/lp/registry/templates/distributionsourcepackage-index.pt 2010-02-09 15:38:13 +0000
1045+++ lib/lp/registry/templates/distributionsourcepackage-index.pt 2010-03-06 06:27:33 +0000
1046@@ -28,21 +28,6 @@
1047 </tal:side>
1048
1049 <tal:main metal:fill-slot="main">
1050- <p class="error message"
1051- tal:condition="view/errors"
1052- tal:content="view/error_count" />
1053- <p class="error message"
1054- tal:repeat="form_wide_error view/form_wide_errors"
1055- tal:content="structure form_wide_error">
1056- Schema validation errors.
1057- </p>
1058- <p class="error message"
1059- tal:define="error python:view.getFieldError('packaging')"
1060- tal:condition="error"
1061- tal:content="structure error">
1062- Field specific error for the hidden packaging field.
1063- </p>
1064-
1065 <div class="top-portlet" id="bugs-and-questions-summary"
1066 tal:define="newbugs context/new_bugtasks/count;
1067 open_questions view/open_questions/count">
1068@@ -141,25 +126,14 @@
1069 <a tal:attributes="href row/series_package/fmt:url"
1070 tal:content="row/distroseries/title"/>
1071 (<span tal:replace="row/distroseries/status/title/lower"/>)
1072- <div style="float:right;">
1073+ <div style="float:right; white-space: nowrap">
1074 <a tal:condition="not: row/packaging"
1075 tal:replace="structure row/series_package/menu:overview/set_upstream/fmt:link"/>
1076 <tal:has_packaging condition="row/packaging">
1077 <img tal:replace="structure row/packaging/productseries/image:icon"/>
1078 <a tal:replace="structure row/packaging/productseries/fmt:link"/>
1079- <form style="display: inline"
1080- method="POST"
1081- tal:condition="row/hidden_packaging_field"
1082- tal:attributes="action request/URL;
1083- id row/delete_packaging_form_id">
1084- <tal:hidden replace="structure row/hidden_packaging_field" />
1085- <tal:action replace="structure view/renderDeletePackagingAction" />
1086- </form>
1087- <a tal:attributes="
1088- href row/series_package/menu:overview/edit_packaging/url;
1089- title string:Edit upsteam link">
1090- <img src="/@@/edit"/>
1091- </a>
1092+ <a tal:replace="structure row/series_package/menu:overview/edit_packaging/fmt:icon"/>
1093+ <a tal:replace="structure row/series_package/menu:overview/remove_packaging/fmt:icon" />
1094 </tal:has_packaging>
1095 </div><!--float right-->
1096 </td>
1097
1098=== modified file 'lib/lp/registry/templates/distroseries-packaging.pt'
1099--- lib/lp/registry/templates/distroseries-packaging.pt 2010-02-16 19:07:42 +0000
1100+++ lib/lp/registry/templates/distroseries-packaging.pt 2010-03-06 06:27:33 +0000
1101@@ -18,7 +18,7 @@
1102 <p>
1103 The packages are listed by priority based on the greatest need to
1104 forward bugs, sync translations, or specify the latest
1105- development branch.
1106+ development branch.
1107 </p>
1108
1109 <ul id="related-pages" class="horizontal">
1110@@ -72,42 +72,7 @@
1111 tal:condition="product/summary"
1112 tal:content="product/summary"/>
1113 </td>
1114-
1115- <td>
1116- <dl>
1117- <dt>
1118- <a tal:replace="structure product/fmt:link" /> project:
1119- </dt>
1120- <dd title="Project bug supervisor">
1121- Bug supervisor:
1122- <tal:yes-no
1123- replace="structure product/bug_supervisor/image:boolean" />
1124- </dd>
1125- <dd title="Project bug tracker"
1126- tal:define="bool product/project|bugtracker|product/bugtracker|product/official_malone">
1127- Bug tracker:
1128- <tal:yes-no replace="structure bool/image:boolean"/>
1129- </dd>
1130- </dl>
1131- </td>
1132-
1133- <td>
1134- <dl>
1135- <dt style="margin-bottom: 6px;">
1136- <a tal:replace="structure series/fmt:link" />:
1137- </dt>
1138- <dd title="Series branch">
1139- Branch:
1140- <tal:yes-no replace="structure series/branch/image:boolean"/>
1141- </dd>
1142- <dd title="Series translations auto import"
1143- tal:condition="translations"
1144- tal:define="bool not:series/translations_autoimport_mode/enumvalue:NO_IMPORT">
1145- Translations:
1146- <tal:yes-no replace="structure bool/image:boolean"/>
1147- </dd>
1148- </dl>
1149- </td>
1150+ <td tal:content="structure package/@@+upstream-connections"/>
1151 </tr>
1152 </tal:packaging>
1153 </tbody>
1154
1155=== modified file 'lib/lp/registry/templates/product-packages.pt'
1156--- lib/lp/registry/templates/product-packages.pt 2010-01-11 20:58:42 +0000
1157+++ lib/lp/registry/templates/product-packages.pt 2010-03-06 06:27:33 +0000
1158@@ -40,16 +40,11 @@
1159 <th>Distribution series</th>
1160 <th>Source package</th>
1161 <th>Version</th>
1162- <th style="width: 1em;"
1163- tal:condition="view/can_delete_packaging">&nbsp;</th>
1164+ <th>&nbsp;</th>
1165 </tr>
1166 </thead>
1167 <tbody>
1168- <tr tal:repeat="packaging_dict packagings">
1169- <tal:packaging_field
1170- define="packaging packaging_dict/packaging;
1171- form_id packaging_dict/form_id;
1172- field packaging_dict/field">
1173+ <tr tal:repeat="packaging packagings">
1174 <td>
1175 <a tal:replace="structure packaging/distroseries/distribution/fmt:link" />
1176 </td>
1177@@ -69,17 +64,11 @@
1178 <tal:currentrelease
1179 replace="packaging/sourcepackage/currentrelease/version|nothing">
1180 2.3.4-1
1181- </tal:currentrelease>
1182- </td>
1183- <td tal:condition="view/can_delete_packaging">
1184- <form style="display: inline" method="post"
1185- tal:attributes="action request/URL;
1186- id form_id">
1187- <tal:hidden replace="structure field" />
1188- <tal:action replace="structure view/renderDeletePackagingAction" />
1189- </form>
1190- </td>
1191- </tal:packaging_field>
1192+ </tal:currentrelease>
1193+ </td>
1194+ <td>
1195+ <a tal:replace="structure packaging/sourcepackage/menu:overview/remove_packaging/fmt:icon" />
1196+ </td>
1197 </tr>
1198 </tbody>
1199 </table>
1200@@ -99,7 +88,7 @@
1201 <h2>Packages by distribution</h2>
1202
1203 <tal:distribution repeat="distro view/distro_packaging">
1204-
1205+
1206 <h3>
1207 <a tal:attributes="href distro/distribution/fmt:url"
1208 tal:content="distro/distribution/title">Ubuntu Linux</a>
1209
1210=== modified file 'lib/lp/registry/templates/sourcepackage-portlet-associations.pt'
1211--- lib/lp/registry/templates/sourcepackage-portlet-associations.pt 2010-02-16 19:13:31 +0000
1212+++ lib/lp/registry/templates/sourcepackage-portlet-associations.pt 2010-03-06 06:27:33 +0000
1213@@ -10,47 +10,7 @@
1214 <div class="portletBody portletContent"
1215 tal:define="series context/productseries">
1216 <tal:has_series condition="series">
1217- <div id="upstreams" class="two-column-list">
1218- <dl>
1219- <dt>
1220- <a tal:replace="structure series/product/fmt:link" /> project
1221- </dt>
1222-
1223- <tal:has_pg define="project series/product/project"
1224- condition="project">
1225- <dd title="Project group">
1226- Project Group:
1227- <a tal:replace="structure project/fmt:link" />
1228- </dd>
1229- </tal:has_pg>
1230-
1231- <dd title="Bug supervisor">
1232- Bug supervisor:
1233- <tal:yes-no replace="structure series/product/bug_supervisor/image:boolean"/>
1234- </dd>
1235-
1236- <dd title="Bug tracker">
1237- Bug tracker:
1238- <tal:yes-no replace="structure view/has_bugtracker/image:boolean"/>
1239- </dd>
1240- </dl>
1241- <dl>
1242- <dt>
1243- <a tal:replace="structure series/fmt:link" />
1244- </dt>
1245- <dd title="Series branch">
1246- Branch:
1247- <tal:yes-no replace="structure series/branch/image:boolean"/>
1248- </dd>
1249- <dd title="Series translations auto import"
1250- tal:condition="context/getTranslationTemplates"
1251- tal:define="bool not:series/translations_autoimport_mode/enumvalue:NO_IMPORT">
1252- Translations:
1253- <tal:yes-no replace="structure bool/image:boolean"/>
1254- </dd>
1255- </dl>
1256-
1257- </div>
1258+ <div tal:content="structure context/@@+upstream-connections"/>
1259
1260 <ul class="horizontal">
1261 <li>
1262@@ -58,12 +18,6 @@
1263 tal:attributes="href series/product/menu:overview/packages/fmt:url"
1264 >Show upstream links</a>
1265 </li>
1266- <li>
1267- <a class="sprite edit"
1268- tal:attributes="
1269- href context/menu:overview/edit_packaging/fmt:url"
1270- >Change upstream project and series</a>
1271- </li>
1272 </ul>
1273 </tal:has_series>
1274
1275
1276=== added file 'lib/lp/registry/templates/sourcepackage-remove-packaging.pt'
1277--- lib/lp/registry/templates/sourcepackage-remove-packaging.pt 1970-01-01 00:00:00 +0000
1278+++ lib/lp/registry/templates/sourcepackage-remove-packaging.pt 2010-03-06 06:27:33 +0000
1279@@ -0,0 +1,34 @@
1280+<html
1281+ xmlns="http://www.w3.org/1999/xhtml"
1282+ xmlns:tal="http://xml.zope.org/namespaces/tal"
1283+ xmlns:metal="http://xml.zope.org/namespaces/metal"
1284+ metal:use-macro="view/macro:page/main_only"
1285+>
1286+
1287+<body>
1288+
1289+<div metal:fill-slot="main">
1290+
1291+ <div tal:condition="not: context/productseries"
1292+ class="error">
1293+ This upstream association was deleted already.
1294+ </div>
1295+ <div tal:condition="context/productseries">
1296+ <div metal:use-macro="context/@@launchpad_form/form">
1297+ <div metal:fill-slot="extra_info">
1298+ <p>
1299+ Do you want to remove the upstream link to
1300+ <a tal:replace="structure context/productseries/fmt:link"/>?
1301+ </p>
1302+ <p>
1303+ Links from distribution packages to upstream project series let
1304+ distribution and upstream maintainers share bugs, patches, and
1305+ translations efficiently.
1306+ </p>
1307+ </div>
1308+ </div>
1309+ </div>
1310+
1311+</div>
1312+</body>
1313+</html>
1314
1315=== added file 'lib/lp/registry/templates/sourcepackage-upstream-connections.pt'
1316--- lib/lp/registry/templates/sourcepackage-upstream-connections.pt 1970-01-01 00:00:00 +0000
1317+++ lib/lp/registry/templates/sourcepackage-upstream-connections.pt 2010-03-06 06:27:33 +0000
1318@@ -0,0 +1,63 @@
1319+<tal:root
1320+ xmlns:tal="http://xml.zope.org/namespaces/tal"
1321+ xmlns:metal="http://xml.zope.org/namespaces/metal"
1322+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
1323+ omit-tag="">
1324+
1325+<tal:comment condition="nothing">
1326+ view.request.annotations['show_edit_buttons'] can be set in views
1327+ that include this page to display the edit and remove buttons.
1328+</tal:comment>
1329+<div id="upstreams" tal:define="series context/productseries">
1330+ <dl>
1331+ <dd>
1332+ <tal:has_pg condition="series/product/project">
1333+ <a tal:replace="structure series/product/project/fmt:link" /> &rArr;
1334+ </tal:has_pg>
1335+ <a tal:replace="structure series/product/fmt:link" /> &rArr;
1336+ <a tal:content="series/name"
1337+ tal:attributes="href series/fmt:url" />
1338+ <span tal:condition="request/annotations/show_edit_buttons | nothing">
1339+ <a tal:replace="
1340+ structure context/menu:overview/edit_packaging/fmt:icon" />
1341+ <a tal:replace="
1342+ structure context/menu:overview/remove_packaging/fmt:icon " />
1343+ </span>
1344+ </dd>
1345+ </dl>
1346+
1347+ <style>
1348+ #upstream-fields dd {
1349+ margin: 0;
1350+ padding: 0;
1351+ }
1352+ </style>
1353+ <div id="upstream-fields" class="two-column-list">
1354+ <dl>
1355+ <dd title="Bug supervisor">
1356+ Bug supervisor:
1357+ <tal:yes-no replace="structure series/product/bug_supervisor/image:boolean"/>
1358+ </dd>
1359+
1360+ <dd title="Bug tracker">
1361+ Bug tracker:
1362+ <tal:yes-no replace="structure view/has_bugtracker/image:boolean"/>
1363+ </dd>
1364+ </dl>
1365+ <dl>
1366+ <dd title="Series branch">
1367+ Branch:
1368+ <tal:yes-no replace="structure series/branch/image:boolean"/>
1369+ </dd>
1370+ <dd title="Series translations auto import"
1371+ tal:condition="context/getTranslationTemplates"
1372+ tal:define="bool not:series/translations_autoimport_mode/enumvalue:NO_IMPORT">
1373+ Translations:
1374+ <tal:yes-no replace="structure bool/image:boolean"/>
1375+ </dd>
1376+ </dl>
1377+
1378+ </div>
1379+</div>
1380+
1381+</tal:root>
1382
1383=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt'
1384--- lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt 2010-02-15 10:40:56 +0000
1385+++ lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt 2010-03-06 06:27:33 +0000
1386@@ -72,11 +72,9 @@
1387 Product, Branches, and Bugs:
1388
1389 >>> print extract_text(find_tag_by_id(browser.contents, 'upstreams'))
1390- Mozilla Firefox ... project
1391- Project Group: the Mozilla Project
1392+ the Mozilla Project...Mozilla Firefox...trunk...
1393 Bug supervisor: no
1394 Bug tracker: yes
1395- Mozilla Firefox trunk series
1396 Branch: no
1397
1398 The user can also download the files for the "currentrelease" (last