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
=== modified file 'lib/canonical/launchpad/icing/style-3-0.css.in'
--- lib/canonical/launchpad/icing/style-3-0.css.in 2010-02-17 00:43:19 +0000
+++ lib/canonical/launchpad/icing/style-3-0.css.in 2010-03-06 06:27:33 +0000
@@ -814,7 +814,7 @@
814}814}
815input[type="submit"].icon-only {815input[type="submit"].icon-only {
816 vertical-align: middle;816 vertical-align: middle;
817 border: 0; 817 border: 0;
818 padding: 0;818 padding: 0;
819 height: 16px;819 height: 16px;
820 width: 16px;820 width: 16px;
821821
=== modified file 'lib/canonical/launchpad/templates/launchpad-form.pt'
--- lib/canonical/launchpad/templates/launchpad-form.pt 2009-11-26 09:18:22 +0000
+++ lib/canonical/launchpad/templates/launchpad-form.pt 2010-03-06 06:27:33 +0000
@@ -16,6 +16,13 @@
16 accept-charset="UTF-8">16 accept-charset="UTF-8">
1717
18 <div metal:define-macro="formbody">18 <div metal:define-macro="formbody">
19 <tal:comment condition="nothing">
20 This field is used by the ReturnToReferrerMixin.
21 </tal:comment>
22 <input type="hidden"
23 name="_return_url"
24 tal:condition="view/_return_url | nothing"
25 tal:attributes="value view/_return_url" />
1926
20 <p metal:define-slot="extra_info" tal:replace="nothing">27 <p metal:define-slot="extra_info" tal:replace="nothing">
21 This is the description of the form.28 This is the description of the form.
2229
=== modified file 'lib/canonical/launchpad/webapp/launchpadform.py'
--- lib/canonical/launchpad/webapp/launchpadform.py 2010-02-19 01:50:36 +0000
+++ lib/canonical/launchpad/webapp/launchpadform.py 2010-03-06 06:27:33 +0000
@@ -7,10 +7,11 @@
7__metaclass__ = type7__metaclass__ = type
88
9__all__ = [9__all__ = [
10 'LaunchpadFormView',
11 'LaunchpadEditFormView',
12 'action',10 'action',
13 'custom_widget',11 'custom_widget',
12 'LaunchpadEditFormView',
13 'LaunchpadFormView',
14 'ReturnToReferrerMixin',
14 'safe_action',15 'safe_action',
15 ]16 ]
1617
@@ -32,7 +33,7 @@
32 IMultiLineWidgetLayout, ICheckBoxWidgetLayout,33 IMultiLineWidgetLayout, ICheckBoxWidgetLayout,
33 IAlwaysSubmittedWidget, UnsafeFormGetSubmissionError)34 IAlwaysSubmittedWidget, UnsafeFormGetSubmissionError)
34from canonical.launchpad.webapp.menu import escape35from canonical.launchpad.webapp.menu import escape
35from canonical.launchpad.webapp.publisher import LaunchpadView36from canonical.launchpad.webapp.publisher import canonical_url, LaunchpadView
3637
3738
38classImplements(CheckBoxWidget, ICheckBoxWidgetLayout)39classImplements(CheckBoxWidget, ICheckBoxWidgetLayout)
@@ -434,3 +435,30 @@
434 """435 """
435 action.is_safe = True436 action.is_safe = True
436 return action437 return action
438
439
440class ReturnToReferrerMixin:
441 """Return to the previous page after submitting the form.
442
443 The _return_url is stored in a hidden field in the launchpad-form.pt
444 between the request to view the form and submitting the form.
445 """
446
447 @property
448 def _return_url(self):
449 """See `LaunchpadFormView`."""
450 # The referer header we want is only available before the view's
451 # form submits to itself. This field is a hidden input in the form.
452 referrer = self.request.form.get('_return_url')
453 if referrer is None:
454 # "referer" is misspelled in the HTTP specification.
455 referrer = self.request.getHeader('referer')
456
457 if (referrer is not None
458 and referrer.startswith(self.request.getApplicationURL())):
459 return referrer
460 else:
461 return canonical_url(self.context)
462
463 next_url = _return_url
464 cancel_url = _return_url
437465
=== modified file 'lib/lp/registry/browser/configure.zcml'
--- lib/lp/registry/browser/configure.zcml 2010-03-05 14:18:18 +0000
+++ lib/lp/registry/browser/configure.zcml 2010-03-06 06:27:33 +0000
@@ -1946,6 +1946,14 @@
1946 facet="overview"1946 facet="overview"
1947 class="lp.registry.browser.sourcepackage.SourcePackageAssociationPortletView"1947 class="lp.registry.browser.sourcepackage.SourcePackageAssociationPortletView"
1948 template="../templates/sourcepackage-portlet-associations.pt"/>1948 template="../templates/sourcepackage-portlet-associations.pt"/>
1949
1950 <browser:page
1951 for="lp.registry.interfaces.sourcepackage.ISourcePackage"
1952 permission="zope.Public"
1953 name="+upstream-connections"
1954 facet="overview"
1955 class="lp.registry.browser.sourcepackage.SourcePackageUpstreamConnectionsView"
1956 template="../templates/sourcepackage-upstream-connections.pt"/>
1949 <browser:page1957 <browser:page
1950 for="lp.registry.interfaces.sourcepackage.ISourcePackage"1958 for="lp.registry.interfaces.sourcepackage.ISourcePackage"
1951 permission="zope.Public"1959 permission="zope.Public"
@@ -2122,6 +2130,14 @@
2122 facet="overview"2130 facet="overview"
2123 template="../templates/sourcepackage-edit-packaging.pt"2131 template="../templates/sourcepackage-edit-packaging.pt"
2124 />2132 />
2133 <browser:page
2134 for="lp.registry.interfaces.sourcepackage.ISourcePackage"
2135 permission="launchpad.AnyPerson"
2136 class="lp.registry.browser.sourcepackage.SourcePackageRemoveUpstreamView"
2137 name="+remove-packaging"
2138 facet="overview"
2139 template="../templates/sourcepackage-remove-packaging.pt"
2140 />
21252141
2126 <browser:page2142 <browser:page
2127 for="lp.registry.interfaces.structuralsubscription.IStructuralSubscriptionTarget"2143 for="lp.registry.interfaces.structuralsubscription.IStructuralSubscriptionTarget"
21282144
=== modified file 'lib/lp/registry/browser/distributionsourcepackage.py'
--- lib/lp/registry/browser/distributionsourcepackage.py 2010-01-06 12:02:38 +0000
+++ lib/lp/registry/browser/distributionsourcepackage.py 2010-03-06 06:27:33 +0000
@@ -49,7 +49,6 @@
49from lp.soyuz.interfaces.distributionsourcepackagerelease import (49from lp.soyuz.interfaces.distributionsourcepackagerelease import (
50 IDistributionSourcePackageRelease)50 IDistributionSourcePackageRelease)
51from lp.soyuz.interfaces.packagediff import IPackageDiffSet51from lp.soyuz.interfaces.packagediff import IPackageDiffSet
52from lp.registry.browser.packaging import PackagingDeleteView
53from lp.registry.interfaces.pocket import pocketsuffix52from lp.registry.interfaces.pocket import pocketsuffix
54from lp.translations.browser.customlanguagecode import (53from lp.translations.browser.customlanguagecode import (
55 HasCustomLanguageCodesTraversalMixin)54 HasCustomLanguageCodesTraversalMixin)
@@ -243,7 +242,7 @@
243242
244243
245class DistributionSourcePackageView(DistributionSourcePackageBaseView,244class DistributionSourcePackageView(DistributionSourcePackageBaseView,
246 PackagingDeleteView):245 LaunchpadView):
247 """View class for DistributionSourcePackage."""246 """View class for DistributionSourcePackage."""
248 implements(IDistributionSourcePackageActionMenu)247 implements(IDistributionSourcePackageActionMenu)
249248
@@ -257,14 +256,6 @@
257 return canonical_url(self.context)256 return canonical_url(self.context)
258257
259 @property258 @property
260 def all_packaging(self):
261 """See `PackagingDeleteView`."""
262 for sourcepackage in self.context.get_distroseries_packages():
263 packaging = sourcepackage.direct_packaging
264 if packaging is not None:
265 yield packaging
266
267 @property
268 def all_published_in_active_distroseries(self):259 def all_published_in_active_distroseries(self):
269 """Return a list of publishings in each active distroseries.260 """Return a list of publishings in each active distroseries.
270261
@@ -408,16 +399,6 @@
408 for distroseries in self.active_series:399 for distroseries in self.active_series:
409 # The first row for each series is the "title" row.400 # The first row for each series is the "title" row.
410 packaging = packages_by_series[distroseries].direct_packaging401 packaging = packages_by_series[distroseries].direct_packaging
411 if packaging is None:
412 delete_packaging_form_id = None
413 hidden_packaging_field = None
414 else:
415 delete_packaging_form_id = "delete_%s_%s_%s" % (
416 packaging.distroseries.name,
417 packaging.productseries.product.name,
418 packaging.productseries.name)
419 hidden_packaging_field = self._renderHiddenPackagingField(
420 packaging)
421 package = packages_by_series[distroseries]402 package = packages_by_series[distroseries]
422 title_row = {403 title_row = {
423 'blank_row': False,404 'blank_row': False,
@@ -426,8 +407,6 @@
426 'distroseries': distroseries,407 'distroseries': distroseries,
427 'series_package': package,408 'series_package': package,
428 'packaging': packaging,409 'packaging': packaging,
429 'hidden_packaging_field': hidden_packaging_field,
430 'delete_packaging_form_id': delete_packaging_form_id,
431 }410 }
432 rows.append(title_row)411 rows.append(title_row)
433412
434413
=== removed file 'lib/lp/registry/browser/packaging.py'
--- lib/lp/registry/browser/packaging.py 2010-01-21 23:46:24 +0000
+++ lib/lp/registry/browser/packaging.py 1970-01-01 00:00:00 +0000
@@ -1,96 +0,0 @@
1# Copyright 2009 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4__metaclass__ = type
5
6__all__ = [
7 'PackagingDeleteView',
8 ]
9
10from zope.component import getUtility
11from zope.formlib import form
12from zope.schema import Choice
13from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
14
15from canonical.launchpad import _
16from lp.registry.interfaces.packaging import IPackagingUtil
17from canonical.launchpad.webapp.launchpadform import action, LaunchpadFormView
18
19
20class PackagingDeleteView(LaunchpadFormView):
21 """A base view that provides packaging link deletion."""
22
23 @property
24 def all_packaging(self):
25 """An iterator of the context's packaging links."""
26 raise NotImplementedError
27
28 def setUpFields(self):
29 """See `LaunchpadFormView`."""
30 # No schema is set in this form, because all fields are created with
31 # custom vocabularies. So we must not call the inherited setUpField
32 # method.
33 self.form_fields = self._createPackagingField()
34
35 @property
36 def can_delete_packaging(self):
37 """Whether the user can delete existing packaging links."""
38 return self.user is not None
39
40 def _createPackagingField(self):
41 """Create a field to specify a Packaging association.
42
43 Create a contextual vocabulary that can specify one of the Packaging
44 associated to this DistributionSourcePackage.
45 """
46 terms = []
47 for packaging in self.all_packaging:
48 terms.append(SimpleTerm(packaging, packaging.id))
49 return form.Fields(
50 Choice(__name__='packaging', vocabulary=SimpleVocabulary(terms),
51 required=True))
52
53 def _renderHiddenPackagingField(self, packaging):
54 """Render a hidden input that fills in the packaging field."""
55 if not self.can_delete_packaging:
56 return None
57 vocabulary = self.form_fields['packaging'].field.vocabulary
58 return '<input type="hidden" name="field.packaging" value="%s" />' % (
59 vocabulary.getTerm(packaging).token)
60
61 def renderDeletePackagingAction(self):
62 """Render a submit input for the delete_packaging_action."""
63 assert self.can_delete_packaging, 'User cannot delete Packaging.'
64 return ('<input type="image" value="Delete Link" '
65 'src="/@@/remove" title="Delete upsteam link" '
66 'name="%s"/>' % self.delete_packaging_action.__name__)
67
68 def handleDeletePackagingError(self, action, data, errors):
69 """Handle errors on package link deletion.
70
71 If 'packaging' is not set in the form data, we assume that means the
72 provided Packaging id was not found, which should only happen if the
73 same Packaging object was concurrently deleted. In this case, we want
74 to display a more informative error message than the default 'Invalid
75 value'.
76 """
77 if data.get('packaging') is None:
78 self.setFieldError(
79 'packaging',
80 _("This upstream association was deleted already."))
81
82 @action(_("Delete Link"), name='delete_packaging',
83 failure=handleDeletePackagingError)
84 def delete_packaging_action(self, action, data):
85 """Delete a Packaging association."""
86 packaging = data['packaging']
87 productseries = packaging.productseries
88 distroseries = packaging.distroseries
89 getUtility(IPackagingUtil).deletePackaging(
90 productseries, packaging.sourcepackagename, distroseries)
91 self.request.response.addNotification(
92 _("Removed upstream association between ${product} "
93 "${productseries} and ${distroseries}.", mapping=dict(
94 product=productseries.product.displayname,
95 productseries=productseries.displayname,
96 distroseries=distroseries.displayname)))
970
=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py 2010-02-16 21:21:14 +0000
+++ lib/lp/registry/browser/product.py 2010-03-06 06:27:33 +0000
@@ -81,7 +81,6 @@
81from lp.registry.browser.distribution import UsesLaunchpadMixin81from lp.registry.browser.distribution import UsesLaunchpadMixin
82from lp.registry.browser.menu import (82from lp.registry.browser.menu import (
83 IRegistryCollectionNavigationMenu, RegistryCollectionActionMenuBase)83 IRegistryCollectionNavigationMenu, RegistryCollectionActionMenuBase)
84from lp.registry.browser.packaging import PackagingDeleteView
85from lp.answers.browser.faqtarget import FAQTargetNavigationMixin84from lp.answers.browser.faqtarget import FAQTargetNavigationMixin
86from canonical.launchpad.browser.feeds import FeedsMixin85from canonical.launchpad.browser.feeds import FeedsMixin
87from lp.registry.browser.productseries import get_series_branch_error86from lp.registry.browser.productseries import get_series_branch_error
@@ -95,13 +94,15 @@
95 StructuralSubscriptionTargetTraversalMixin)94 StructuralSubscriptionTargetTraversalMixin)
96from canonical.launchpad.mail import format_address, simple_sendmail95from canonical.launchpad.mail import format_address, simple_sendmail
97from canonical.launchpad.webapp import (96from canonical.launchpad.webapp import (
98 ApplicationMenu, LaunchpadEditFormView, LaunchpadFormView, LaunchpadView,97 ApplicationMenu, canonical_url, enabled_with_permission, LaunchpadView,
99 Link, Navigation, StandardLaunchpadFacets, action, canonical_url,98 Link, Navigation, sorted_version_numbers, StandardLaunchpadFacets,
100 custom_widget, enabled_with_permission, sorted_version_numbers,
101 stepthrough, stepto, structured)99 stepthrough, stepto, structured)
102from canonical.launchpad.webapp.authorization import check_permission100from canonical.launchpad.webapp.authorization import check_permission
103from canonical.launchpad.webapp.batching import BatchNavigator101from canonical.launchpad.webapp.batching import BatchNavigator
104from canonical.launchpad.webapp.breadcrumb import Breadcrumb102from canonical.launchpad.webapp.breadcrumb import Breadcrumb
103from canonical.launchpad.webapp.launchpadform import (
104 action, custom_widget, LaunchpadEditFormView, LaunchpadFormView,
105 ReturnToReferrerMixin)
105from canonical.launchpad.webapp.menu import NavigationMenu106from canonical.launchpad.webapp.menu import NavigationMenu
106from canonical.widgets.popup import PersonPickerWidget107from canonical.widgets.popup import PersonPickerWidget
107from canonical.widgets.date import DateWidget108from canonical.widgets.date import DateWidget
@@ -897,17 +898,11 @@
897 check_permission('launchpad.Commercial', self.context))898 check_permission('launchpad.Commercial', self.context))
898899
899900
900class ProductPackagesView(PackagingDeleteView):901class ProductPackagesView(LaunchpadView):
901 """View for displaying product packaging"""902 """View for displaying product packaging"""
902903
903 label = 'Linked packages'904 label = 'Linked packages'
904905 page_title = label
905 @property
906 def all_packaging(self):
907 """See `PackagingDeleteView`."""
908 for series in self.context.series:
909 for packaging in series.packagings:
910 yield packaging
911906
912 @cachedproperty907 @cachedproperty
913 def series_packages(self):908 def series_packages(self):
@@ -921,23 +916,11 @@
921 field: '<input type=''hidden' ...>},916 field: '<input type=''hidden' ...>},
922 }]917 }]
923 """918 """
924 # This method is a superset of all_packaging. While all_packaging will
925 # be called several times as data is mutated, series_packages should
926 # only be called during render().
927 packaged_series = []919 packaged_series = []
928 for series in self.context.series:920 for series in self.context.series:
929 packagings = []921 packagings = []
930 for packaging in series.packagings:922 for packaging in series.packagings:
931 form_id = 'delete-%s-%s-%s' % (923 packagings.append(packaging)
932 packaging.distroseries.name,
933 packaging.sourcepackagename.name,
934 packaging.productseries.name,
935 )
936 packaging_field = dict(
937 packaging=packaging,
938 form_id=form_id,
939 field=self._renderHiddenPackagingField(packaging))
940 packagings.append(packaging_field)
941 packaged_series.append(dict(924 packaged_series.append(dict(
942 series=series, packagings=packagings))925 series=series, packagings=packagings))
943 return packaged_series926 return packaged_series
@@ -1262,7 +1245,8 @@
1262 return canonical_url(self.context)1245 return canonical_url(self.context)
12631246
12641247
1265class ProductReviewLicenseView(ProductEditView, EditPrivateBugsMixin):1248class ProductReviewLicenseView(ReturnToReferrerMixin,
1249 ProductEditView, EditPrivateBugsMixin):
1266 """A view to review a project and change project privileges."""1250 """A view to review a project and change project privileges."""
1267 label = "Review project"1251 label = "Review project"
1268 field_names = [1252 field_names = [
@@ -1302,27 +1286,6 @@
1302 # supervisor.1286 # supervisor.
1303 self.validate_private_bugs(data)1287 self.validate_private_bugs(data)
13041288
1305 @property
1306 def next_url(self):
1307 """See `LaunchpadFormView`."""
1308 # The referer header we want is only available before the view's
1309 # form submits to itself. This field is a hidden input in the form.
1310 referrer = self.request.form.get('next_url')
1311 if referrer is None:
1312 referrer = self.request.getHeader('referer')
1313
1314 if (referrer is not None
1315 and referrer.startswith(self.request.getApplicationURL())):
1316 return referrer
1317 else:
1318 return canonical_url(self.context)
1319
1320 @property
1321 def cancel_url(self):
1322 """See `LaunchpadFormView`."""
1323 return self.next_url
1324
1325
1326class ProductAddSeriesView(LaunchpadFormView):1289class ProductAddSeriesView(LaunchpadFormView):
1327 """A form to add new product series"""1290 """A form to add new product series"""
13281291
@@ -1613,7 +1576,7 @@
1613 self.request.form['summary'] = data['summary']1576 self.request.form['summary'] = data['summary']
16141577
16151578
1616class ProjectAddStepTwo(StepView, ProductLicenseMixin):1579class ProjectAddStepTwo(StepView, ProductLicenseMixin, ReturnToReferrerMixin):
1617 """Step 2 (of 2) in the +new project add wizard."""1580 """Step 2 (of 2) in the +new project add wizard."""
16181581
1619 _field_names = ['displayname', 'name', 'title', 'summary',1582 _field_names = ['displayname', 'name', 'title', 'summary',
16201583
=== modified file 'lib/lp/registry/browser/sourcepackage.py'
--- lib/lp/registry/browser/sourcepackage.py 2010-02-23 19:43:58 +0000
+++ lib/lp/registry/browser/sourcepackage.py 2010-03-06 06:27:33 +0000
@@ -12,7 +12,8 @@
12 'SourcePackageFacets',12 'SourcePackageFacets',
13 'SourcePackageHelpView',13 'SourcePackageHelpView',
14 'SourcePackageNavigation',14 'SourcePackageNavigation',
15 'SourcePackagePackaging',15 'SourcePackageRemoveUpstreamView',
16 'SourcePackageUpstreamConnectionsView',
16 'SourcePackageView',17 'SourcePackageView',
17 ]18 ]
1819
@@ -40,7 +41,7 @@
40from lp.answers.browser.questiontarget import (41from lp.answers.browser.questiontarget import (
41 QuestionTargetFacetMixin, QuestionTargetAnswersMenu)42 QuestionTargetFacetMixin, QuestionTargetAnswersMenu)
42from lp.services.worlddata.interfaces.country import ICountry43from lp.services.worlddata.interfaces.country import ICountry
43from lp.registry.interfaces.packaging import IPackaging44from lp.registry.interfaces.packaging import IPackaging, IPackagingUtil
44from lp.registry.interfaces.pocket import PackagePublishingPocket45from lp.registry.interfaces.pocket import PackagePublishingPocket
45from lp.registry.interfaces.product import IProductSet46from lp.registry.interfaces.product import IProductSet
46from lp.registry.interfaces.productseries import IProductSeries47from lp.registry.interfaces.productseries import IProductSeries
@@ -49,12 +50,15 @@
49from lp.translations.interfaces.potemplate import IPOTemplateSet50from lp.translations.interfaces.potemplate import IPOTemplateSet
50from canonical.launchpad import _51from canonical.launchpad import _
51from canonical.launchpad.webapp import (52from canonical.launchpad.webapp import (
52 action, ApplicationMenu, custom_widget, GetitemNavigation,53 ApplicationMenu, GetitemNavigation, Link, redirection,
53 LaunchpadFormView, Link, redirection, StandardLaunchpadFacets, stepto)54 StandardLaunchpadFacets, stepto)
55from canonical.launchpad.webapp.launchpadform import (
56 action, custom_widget, LaunchpadFormView, ReturnToReferrerMixin)
54from canonical.launchpad.webapp import canonical_url57from canonical.launchpad.webapp import canonical_url
55from canonical.launchpad.webapp.authorization import check_permission58from canonical.launchpad.webapp.authorization import check_permission
56from canonical.launchpad.webapp.breadcrumb import Breadcrumb59from canonical.launchpad.webapp.breadcrumb import Breadcrumb
57from canonical.launchpad.webapp.menu import structured60from canonical.launchpad.webapp.menu import structured
61from canonical.launchpad.webapp.publisher import LaunchpadView
5862
59from canonical.lazr.utils import smartquote63from canonical.lazr.utils import smartquote
6064
@@ -108,7 +112,7 @@
108 usedfor = ISourcePackage112 usedfor = ISourcePackage
109 facet = 'overview'113 facet = 'overview'
110 links = [114 links = [
111 'distribution_source_package', 'edit_packaging',115 'distribution_source_package', 'edit_packaging', 'remove_packaging',
112 'changelog', 'builds', 'set_upstream',116 'changelog', 'builds', 'set_upstream',
113 ]117 ]
114118
@@ -124,6 +128,10 @@
124 def edit_packaging(self):128 def edit_packaging(self):
125 return Link('+edit-packaging', 'Change upstream link', icon='edit')129 return Link('+edit-packaging', 'Change upstream link', icon='edit')
126130
131 def remove_packaging(self):
132 return Link(
133 '+remove-packaging', 'Remove upstream link', icon='remove')
134
127 def set_upstream(self):135 def set_upstream(self):
128 return Link("+edit-packaging", "Set upstream link", icon="add")136 return Link("+edit-packaging", "Set upstream link", icon="add")
129137
@@ -143,7 +151,7 @@
143 return Link('+gethelp', 'Help and support options', icon='info')151 return Link('+gethelp', 'Help and support options', icon='info')
144152
145153
146class SourcePackageChangeUpstreamStepOne(StepView):154class SourcePackageChangeUpstreamStepOne(ReturnToReferrerMixin, StepView):
147 """A view to set the `IProductSeries` of a sourcepackage."""155 """A view to set the `IProductSeries` of a sourcepackage."""
148 schema = Interface156 schema = Interface
149 _field_names = []157 _field_names = []
@@ -167,9 +175,8 @@
167 IProductSeries['product'], default=default)175 IProductSeries['product'], default=default)
168 self.form_fields += Fields(product_field)176 self.form_fields += Fields(product_field)
169177
170 @property178 # Override ReturnToReferrerMixin.next_url.
171 def cancel_url(self):179 next_url = None
172 return canonical_url(self.context)
173180
174 def main_action(self, data):181 def main_action(self, data):
175 """See `MultiStepView`."""182 """See `MultiStepView`."""
@@ -177,7 +184,7 @@
177 self.request.form['product'] = data['product']184 self.request.form['product'] = data['product']
178185
179186
180class SourcePackageChangeUpstreamStepTwo(StepView):187class SourcePackageChangeUpstreamStepTwo(ReturnToReferrerMixin, StepView):
181 """A view to set the `IProductSeries` of a sourcepackage."""188 """A view to set the `IProductSeries` of a sourcepackage."""
182 schema = IProductSeries189 schema = IProductSeries
183 _field_names = ['product']190 _field_names = ['product']
@@ -196,10 +203,6 @@
196 custom_widget('product', DropdownWidget, visible=False)203 custom_widget('product', DropdownWidget, visible=False)
197 custom_widget('productseries', LaunchpadRadioWidget)204 custom_widget('productseries', LaunchpadRadioWidget)
198205
199 @property
200 def cancel_url(self):
201 return canonical_url(self.context)
202
203 def setUpFields(self):206 def setUpFields(self):
204 super(SourcePackageChangeUpstreamStepTwo, self).setUpFields()207 super(SourcePackageChangeUpstreamStepTwo, self).setUpFields()
205208
@@ -261,12 +264,16 @@
261 Fields(display_product_field, productseries_choice)264 Fields(display_product_field, productseries_choice)
262 + self.form_fields)265 + self.form_fields)
263266
267 # Override ReturnToReferrerMixin.next_url until the main_action()
268 # is called.
269 next_url = None
270
264 main_action_label = u'Change'271 main_action_label = u'Change'
265 def main_action(self, data):272 def main_action(self, data):
266 productseries = data['productseries']273 productseries = data['productseries']
267 # Because it is part of a multistep view, the next_url can't274 # Because it is part of a multistep view, the next_url can't
268 # be set until the action is called, or it will skip the step.275 # be set until the action is called, or it will skip the step.
269 self.next_url = canonical_url(self.context)276 self.next_url = self._return_url
270 if self.context.productseries == productseries:277 if self.context.productseries == productseries:
271 # There is nothing to do.278 # There is nothing to do.
272 return279 return
@@ -282,6 +289,27 @@
282 first_step = SourcePackageChangeUpstreamStepOne289 first_step = SourcePackageChangeUpstreamStepOne
283290
284291
292class SourcePackageRemoveUpstreamView(ReturnToReferrerMixin,
293 LaunchpadFormView):
294 """A view for removing the link to an upstream package."""
295
296 schema = Interface
297 field_names = []
298 label = 'Unlink an upstream project'
299 page_title = label
300
301 @action('Unlink')
302 def unlink(self, action, data):
303 old_series = self.context.productseries
304 getUtility(IPackagingUtil).deletePackaging(
305 self.context.productseries,
306 self.context.sourcepackagename,
307 self.context.distroseries)
308 self.request.response.addInfoNotification(
309 'Removed upstream association between %s and %s.' % (
310 old_series.title, self.context.distroseries.displayname))
311
312
285class SourcePackageView:313class SourcePackageView:
286 """A view for (distro series) source packages."""314 """A view for (distro series) source packages."""
287315
@@ -411,6 +439,7 @@
411 def setUpFields(self):439 def setUpFields(self):
412 """See `LaunchpadFormView`."""440 """See `LaunchpadFormView`."""
413 super(SourcePackageAssociationPortletView, self).setUpFields()441 super(SourcePackageAssociationPortletView, self).setUpFields()
442 self.request.annotations['show_edit_buttons'] = True
414 # Find registered products that are similarly named to the source443 # Find registered products that are similarly named to the source
415 # package.444 # package.
416 product_vocab = getVocabularyRegistry().get(None, 'Product')445 product_vocab = getVocabularyRegistry().get(None, 'Product')
@@ -444,6 +473,10 @@
444 upstream.displayname)473 upstream.displayname)
445 self.next_url = self.request.getURL()474 self.next_url = self.request.getURL()
446475
476
477class SourcePackageUpstreamConnectionsView(LaunchpadView):
478 """A shared view with upstream connection info."""
479
447 @property480 @property
448 def has_bugtracker(self):481 def has_bugtracker(self):
449 """Does the product have a bugtracker set?"""482 """Does the product have a bugtracker set?"""
450483
=== modified file 'lib/lp/registry/browser/tests/packaging-views.txt'
--- lib/lp/registry/browser/tests/packaging-views.txt 2010-02-18 18:58:26 +0000
+++ lib/lp/registry/browser/tests/packaging-views.txt 2010-03-06 06:27:33 +0000
@@ -273,32 +273,19 @@
273 >>> print view.label273 >>> print view.label
274 Linked packages274 Linked packages
275275
276The view defines the all_packages property used by the PackagingDeleteView
277to create a vocabulary.
278
279 >>> for package in view.all_packaging:
280 ... print package.distroseries.name, package.productseries.name
281 grumpy hotter
282 hoary hotter
283
284The view provides the series_packages property that returns a list of276The view provides the series_packages property that returns a list of
285dicts. Each dict as a series and a list of package dicts. The package dict277dicts. Each dict as a series and a list of packages.
286contains the package and field for form actions.
287278
288 >>> for series_dict in view.series_packages:279 >>> def print_packages(view):
289 ... print series_dict['series'].name280 ... for series_dict in view.series_packages:
290 ... for package_dict in series_dict['packagings']:281 ... print series_dict['series'].name
291 ... print package_dict['packaging'].distroseries.name282 ... for package in series_dict['packagings']:
292 ... print package_dict['form_id']283 ... print package.distroseries.name
293 ... print package_dict['field']284 >>> print_packages(view)
294 cold285 cold
295 hotter286 hotter
296 grumpy287 grumpy
297 delete-grumpy-hot-hotter
298 <input type="hidden" name="field.packaging" .../>
299 hoary288 hoary
300 delete-hoary-thunderbird-hotter
301 <input type="hidden" name="field.packaging" .../>
302 trunk289 trunk
303290
304The view provides the distro_packaging property that is a list of291The view provides the distro_packaging property that is a list of
@@ -306,40 +293,48 @@
306sorted by distribution with Ubuntu first and the rest in alphabetic293sorted by distribution with Ubuntu first and the rest in alphabetic
307order.294order.
308295
309 >>> view = create_initialized_view(product, name='+packages')
310 >>> for distro_dict in view.distro_packaging:296 >>> for distro_dict in view.distro_packaging:
311 ... print distro_dict['distribution'].name297 ... print distro_dict['distribution'].name
312 ubuntu298 ubuntu
313299
314The +packages named view descends from PackagingDeleteView to provide remove
315link actions for the product's linked packages.
316
317 >>> from lp.registry.browser.packaging import PackagingDeleteView
318
319 >>> isinstance(view, PackagingDeleteView)
320 True
321
322A packaging link can be deleted if the owner believes it is an error. The300A packaging link can be deleted if the owner believes it is an error. The
323package linked to hoary is wrong; thunderbird is the wrong sourcepackage.301package linked to hoary is wrong; thunderbird is the wrong sourcepackage.
324302
303 >>> from canonical.launchpad.testing.pages import find_tag_by_id
304 >>> view = create_initialized_view(
305 ... product, name='+packages', principal=a_user)
306 >>> print_packages(view)
307 cold
308 hotter
309 grumpy
310 hoary
311 trunk
312
313 # There are links to the +remove-packaging page.
314 >>> table = find_tag_by_id(view.render(), 'packages-hotter')
315 >>> for link in table.findAll('a'):
316 ... if '+remove-packaging' in link['href']:
317 ... print link['href']
318 http://launchpad.dev/ubuntu/grumpy/+source/hot/+remove-packaging
319 http://launchpad.dev/ubuntu/hoary/+source/thunderbird/+remove-packaging
320
321 >>> [hoary_package] = [
322 ... package for series_dict in view.series_packages
323 ... for package in series_dict['packagings']
324 ... if package.distroseries.name == 'hoary']
325 >>> form = {'field.actions.unlink': 'Unlink'}
326 >>> unlink_view = create_initialized_view(
327 ... hoary_package.sourcepackage, name='+remove-packaging', form=form)
328 >>> unlink_view.errors
329 []
330
331 # The view has to be reloaded since view.series_packages is cached.
325 >>> view = create_initialized_view(product, name='+packages')332 >>> view = create_initialized_view(product, name='+packages')
326 >>> for package in view.all_packaging:333 >>> print_packages(view)
327 ... print package.distroseries.name, package.productseries.name334 cold
328 grumpy hotter335 hotter
329 hoary hotter336 grumpy
330337 trunk
331 >>> [hoary_package] = [package for package in view.all_packaging
332 ... if package.distroseries.name == 'hoary']
333 >>> form = {
334 ... 'field.packaging': '%s' % hoary_package.id,
335 ... 'field.actions.delete_packaging': 'Delete upstream link',
336 ... }
337 >>> view = create_initialized_view(product, name='+packages', form=form)
338 >>> view.errors
339 []
340 >>> for package in view.all_packaging:
341 ... print package.distroseries.name, package.productseries.name
342 grumpy hotter
343338
344339
345Distro series +packaging view340Distro series +packaging view
346341
=== modified file 'lib/lp/registry/browser/tests/sourcepackage-views.txt'
--- lib/lp/registry/browser/tests/sourcepackage-views.txt 2010-02-18 20:39:01 +0000
+++ lib/lp/registry/browser/tests/sourcepackage-views.txt 2010-03-06 06:27:33 +0000
@@ -145,6 +145,7 @@
145 >>> print view.request.response.notifications145 >>> print view.request.response.notifications
146 []146 []
147147
148
148Upstream associations portlet149Upstream associations portlet
149-----------------------------150-----------------------------
150151
@@ -163,12 +164,18 @@
163164
164 >>> from canonical.launchpad.testing.pages import (165 >>> from canonical.launchpad.testing.pages import (
165 ... extract_text, find_tag_by_id)166 ... extract_text, find_tag_by_id)
166 >>> content = extract_text(find_tag_by_id(view.render(), 'upstreams'))167 >>> content = find_tag_by_id(view.render(), 'upstreams')
167 >>> print content168 >>> for link in content.findAll('a'):
168 Bonkers project169 ... print link['href']
170 /bonkers
171 /bonkers/crazy
172 .../+source/bonkers/+edit-packaging
173 .../+source/bonkers/+remove-packaging
174
175 >>> print extract_text(content)
176 Bonkers...crazy...
169 Bug supervisor: no177 Bug supervisor: no
170 Bug tracker: no178 Bug tracker: no
171 Bonkers crazy series
172 Branch: no179 Branch: no
173180
174A new source project that is not linked to an upstream will result in181A new source project that is not linked to an upstream will result in
@@ -211,6 +218,9 @@
211 Lernid Dev...218 Lernid Dev...
212219
213220
221Upstream connections view
222-------------------------
223
214The view includes a property for determining if the project has a bug224The view includes a property for determining if the project has a bug
215tracker, though the rules are somewhat complicated.225tracker, though the rules are somewhat complicated.
216226
@@ -226,7 +236,8 @@
226 >>> package = factory.makeSourcePackage(236 >>> package = factory.makeSourcePackage(
227 ... sourcepackagename=sourcepackagename, distroseries=distroseries)237 ... sourcepackagename=sourcepackagename, distroseries=distroseries)
228238
229 >>> view = create_initialized_view(package, name='+portlet-associations')239 >>> view = create_initialized_view(
240 ... package, name='+upstream-connections')
230241
231 >>> print package.productseries242 >>> print package.productseries
232 None243 None
@@ -242,7 +253,8 @@
242If a product is not part of a project group and its bug tracker is not253If a product is not part of a project group and its bug tracker is not
243set then the view property is false.254set then the view property is false.
244255
245 >>> view = create_initialized_view(package, name='+portlet-associations')256 >>> view = create_initialized_view(
257 ... package, name='+upstream-connections')
246258
247 >>> print product.official_malone259 >>> print product.official_malone
248 False260 False
@@ -285,3 +297,37 @@
285 >>> project.bugtracker = bugtracker297 >>> project.bugtracker = bugtracker
286 >>> print view.has_bugtracker298 >>> print view.has_bugtracker
287 True299 True
300
301
302Remove packaging view
303---------------------
304
305This view allows removal of the packaging link from the sourcepackage
306to the project series.
307
308 >>> view = create_initialized_view(package, name='+remove-packaging')
309 >>> print view.label
310 Unlink an upstream project
311
312 >>> print view.page_title
313 Unlink an upstream project
314
315 >>> print view.cancel_url
316 http://launchpad.dev/youbuntu/wonky/+source/stinkypackage
317
318 >>> form = {'field.actions.unlink': 'Unlink'}
319 >>> view = create_initialized_view(
320 ... package, name='+remove-packaging', form=form,
321 ... principal=product.owner)
322 >>> view.errors
323 []
324
325 >>> for notification in view.request.response.notifications:
326 ... print notification.message
327 Removed upstream association between Stinky stinkyseries series and Wonky.
328
329 >>> view = create_initialized_view(package, name='+portlet-associations')
330 >>> print extract_text(find_tag_by_id(view.render(), 'no-upstreams'))
331 There are no projects registered in Launchpad that are a potential
332 match for this source package. Can you help us find one?
333 Set upstream link
288334
=== modified file 'lib/lp/registry/browser/tests/test_packaging.py'
--- lib/lp/registry/browser/tests/test_packaging.py 2009-10-23 12:53:21 +0000
+++ lib/lp/registry/browser/tests/test_packaging.py 2010-03-06 06:27:33 +0000
@@ -55,8 +55,10 @@
55 # Delete the packaging55 # Delete the packaging
56 user_browser = self.user_browser56 user_browser = self.user_browser
57 user_browser.open('http://launchpad.dev/ubuntu/+source/alsa-utils')57 user_browser.open('http://launchpad.dev/ubuntu/+source/alsa-utils')
58 form = user_browser.getForm("delete_warty_alsa-utils_trunk")58 link = user_browser.getLink(
59 form.getControl(name="field.actions.delete_packaging").click()59 url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
60 link.click()
61 user_browser.getControl('Unlink').click()
60 # Check that the change was committed.62 # Check that the change was committed.
61 login('no-priv@canonical.com')63 login('no-priv@canonical.com')
62 self.assertFalse(packaging_util.packagingEntryExists(64 self.assertFalse(packaging_util.packagingEntryExists(
6365
=== modified file 'lib/lp/registry/model/productseries.py'
--- lib/lp/registry/model/productseries.py 2010-02-25 21:37:02 +0000
+++ lib/lp/registry/model/productseries.py 2010-03-06 06:27:33 +0000
@@ -412,8 +412,10 @@
412412
413 # ok, we didn't find a packaging record that matches, let's go ahead413 # ok, we didn't find a packaging record that matches, let's go ahead
414 # and create one414 # and create one
415 pkg = Packaging(distroseries=distroseries,415 pkg = Packaging(
416 sourcepackagename=sourcepackagename, productseries=self,416 distroseries=distroseries,
417 sourcepackagename=sourcepackagename,
418 productseries=self,
417 packaging=PackagingType.PRIME,419 packaging=PackagingType.PRIME,
418 owner=owner)420 owner=owner)
419 pkg.sync() # convert UTC_NOW to actual datetime421 pkg.sync() # convert UTC_NOW to actual datetime
420422
=== modified file 'lib/lp/registry/model/sourcepackage.py'
--- lib/lp/registry/model/sourcepackage.py 2010-02-25 21:37:02 +0000
+++ lib/lp/registry/model/sourcepackage.py 2010-03-06 06:27:33 +0000
@@ -477,10 +477,11 @@
477 target.datecreated = UTC_NOW477 target.datecreated = UTC_NOW
478 else:478 else:
479 # ok, we need to create a new one479 # ok, we need to create a new one
480 Packaging(distroseries=self.distroseries,480 Packaging(
481 sourcepackagename=self.sourcepackagename,481 distroseries=self.distroseries,
482 productseries=productseries, owner=user,482 sourcepackagename=self.sourcepackagename,
483 packaging=PackagingType.PRIME)483 productseries=productseries, owner=user,
484 packaging=PackagingType.PRIME)
484 # and make sure this change is immediately available485 # and make sure this change is immediately available
485 flush_database_updates()486 flush_database_updates()
486487
487488
=== modified file 'lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt'
--- lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt 2010-02-25 16:36:02 +0000
+++ lib/lp/registry/stories/distroseries/xx-show-distroseries-packaging.txt 2010-03-06 06:27:33 +0000
@@ -17,12 +17,13 @@
17 Source Package Upstream Project Upstream Contributor Connections17 Source Package Upstream Project Upstream Contributor Connections
18 netapplet18 netapplet
19 NetApplet ... The Novell Network Applet19 NetApplet ... The Novell Network Applet
20 NetApplet ... project: Bug supervisor: no Bug tracker: yes20 GNOME...NetApplet...trunk
21 NetApplet trunk series: Branch: no21 ...Bug supervisor: no Bug tracker: yes Branch: no
22 evolution22 evolution
23 Evolution ... Evolution is an email client, addressbook ...23 Evolution ... Evolution is an email client, addressbook ...
24 Evolution ... project: Bug supervisor: no Bug tracker: yes24 GNOME...Evolution...trunk
25 Evolution trunk series: Branch: yes Translations: no25 ...Bug supervisor: no Bug tracker: yes
26 Branch: yes Translations: no
2627
27Any use can see that this page is related to the needs packaging report. It28Any use can see that this page is related to the needs packaging report. It
28is linked, but the link to this page is not enabled.29is linked, but the link to this page is not enabled.
2930
=== modified file 'lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt'
--- lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt 2009-10-23 12:53:21 +0000
+++ lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging-concurrent-deletion.txt 2010-03-06 06:27:33 +0000
@@ -16,33 +16,28 @@
16Then the user click the "Delete Link" button in the first tab. The16Then the user click the "Delete Link" button in the first tab. The
17deletion succeeds and the usual informational message is displayed.17deletion succeeds and the usual informational message is displayed.
1818
19 >>> form = first_browser.getForm("delete_warty_alsa-utils_trunk")19 >>> link = first_browser.getLink(
20 >>> form.getControl(name="field.actions.delete_packaging").click()20 ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
21 >>> link.click()
22 >>> first_browser.getControl('Unlink').click()
21 >>> content = first_browser.contents23 >>> content = first_browser.contents
22 >>> for tag in find_tags_by_class(content, 'error'):24 >>> for tag in find_tags_by_class(content, 'error'):
23 ... print extract_text(tag)25 ... print extract_text(tag)
24 >>> for tag in find_tags_by_class(content, 'informational'):26 >>> for tag in find_tags_by_class(content, 'informational'):
25 ... print extract_text(tag)27 ... print extract_text(tag)
26 Removed upstream association between alsa-utils trunk and Warty.28 Removed upstream association between alsa-utils trunk series and Warty.
2729
28A few minutes later, the user sees the same packaging association in the30A few minutes later, the user sees the same packaging association in the
29second tab, and clicks the "Delete Link" button again.31second tab, and clicks the "Delete Link" button again.
3032
31The packaging object has been deleted already, so this action cannot33The packaging object has been deleted already, so this action cannot
32succeed. We yield a more useful error message than the mysterious34succeed.
33"Invalid value" displayed by default.35
3436 >>> second_browser.getLink(
35XXX: We would like to display an informational message and no error37 ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging').click()
36message, but it would be too much trouble for a corner case like this.
37So we resort to just displaying a helpful error message.
38-- David Allouche 2007-12-07
39
40 >>> form = second_browser.getForm("delete_warty_alsa-utils_trunk")
41 >>> form.getControl(name="field.actions.delete_packaging").click()
42 >>> content = second_browser.contents38 >>> content = second_browser.contents
43 >>> for tag in find_tags_by_class(content, 'informational'):39 >>> for tag in find_tags_by_class(content, 'informational'):
44 ... print extract_text(tag)40 ... print extract_text(tag)
45 >>> for tag in find_tags_by_class(content, 'error'):41 >>> for tag in find_tags_by_class(content, 'error'):
46 ... print extract_text(tag)42 ... print extract_text(tag)
47 There is 1 error.
48 This upstream association was deleted already.43 This upstream association was deleted already.
4944
=== modified file 'lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt'
--- lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt 2009-10-23 12:53:21 +0000
+++ lib/lp/registry/stories/packaging/xx-distributionsourcepackage-packaging.txt 2010-03-06 06:27:33 +0000
@@ -15,6 +15,7 @@
15 The Hoary Hedgehog Release (active development) Set upstream link15 The Hoary Hedgehog Release (active development) Set upstream link
16 1.0.9a-4ubuntu1 release (main) ... weeks ago16 1.0.9a-4ubuntu1 release (main) ... weeks ago
17 The Warty Warthog Release (current stable release) alsa-utils trunk series17 The Warty Warthog Release (current stable release) alsa-utils trunk series
18 ...
18 1.0.8-1ubuntu1 release (main) ... weeks ago19 1.0.8-1ubuntu1 release (main) ... weeks ago
19 1.0.9a-4 release (main) ... weeks ago20 1.0.9a-4 release (main) ... weeks ago
2021
@@ -26,9 +27,10 @@
26packaging links.27packaging links.
2728
28 >>> user_browser.open('http://launchpad.dev/ubuntu/+source/alsa-utils')29 >>> user_browser.open('http://launchpad.dev/ubuntu/+source/alsa-utils')
29 >>> form = user_browser.getForm("delete_warty_alsa-utils_trunk")30 >>> link = user_browser.getLink(
30 >>> print form.getControl(name="field.actions.delete_packaging")31 ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
31 <ImageControl name='field.actions.delete_packaging' type='image'>32 >>> print link
33 <Link text='Remove upstream link'...
3234
33This button is not displayed to anonymous users.35This button is not displayed to anonymous users.
3436
@@ -40,14 +42,16 @@
4042
41Clicking this button deletes the corresponding packaging association.43Clicking this button deletes the corresponding packaging association.
4244
43 >>> form = user_browser.getForm("delete_warty_alsa-utils_trunk")45 >>> link = user_browser.getLink(
44 >>> form.getControl(name="field.actions.delete_packaging").click()46 ... url='/ubuntu/warty/+source/alsa-utils/+remove-packaging')
47 >>> link.click()
48 >>> user_browser.getControl('Unlink').click()
45 >>> content = user_browser.contents49 >>> content = user_browser.contents
46 >>> for tag in find_tags_by_class(content, 'error'):50 >>> for tag in find_tags_by_class(content, 'error'):
47 ... print extract_text(tag)51 ... print extract_text(tag)
48 >>> for tag in find_tags_by_class(content, 'informational'):52 >>> for tag in find_tags_by_class(content, 'informational'):
49 ... print extract_text(tag)53 ... print extract_text(tag)
50 Removed upstream association between alsa-utils trunk and Warty.54 Removed upstream association between alsa-utils trunk series and Warty.
51 >>> print extract_text(find_tag_by_id(content, 'packages_list'))55 >>> print extract_text(find_tag_by_id(content, 'packages_list'))
52 The Hoary Hedgehog Release (active development) Set upstream link56 The Hoary Hedgehog Release (active development) Set upstream link
53 1.0.9a-4ubuntu1 release (main) ... weeks ago57 1.0.9a-4ubuntu1 release (main) ... weeks ago
5458
=== modified file 'lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt'
--- lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt 2010-02-18 20:41:01 +0000
+++ lib/lp/registry/stories/packaging/xx-sourcepackage-packaging.txt 2010-03-06 06:27:33 +0000
@@ -29,11 +29,9 @@
29 >>> user_browser.getControl("Change").click()29 >>> user_browser.getControl("Change").click()
30 >>> print extract_text(find_tag_by_id(30 >>> print extract_text(find_tag_by_id(
31 ... user_browser.contents, 'upstreams'))31 ... user_browser.contents, 'upstreams'))
32 Mozilla Thunderbird ... project32 the Mozilla Project...Mozilla Thunderbird...trunk...
33 Project Group: the Mozilla Project
34 Bug supervisor: no33 Bug supervisor: no
35 Bug tracker: no34 Bug tracker: no
36 Mozilla Thunderbird trunk series
37 Branch: no35 Branch: no
38 Translations: no36 Translations: no
3937
4038
=== modified file 'lib/lp/registry/stories/product/xx-product-package-pages.txt'
--- lib/lp/registry/stories/product/xx-product-package-pages.txt 2010-01-12 22:34:51 +0000
+++ lib/lp/registry/stories/product/xx-product-package-pages.txt 2010-03-06 06:27:33 +0000
@@ -30,8 +30,8 @@
30 >>> print extract_text(find_tag_by_id(30 >>> print extract_text(find_tag_by_id(
31 ... evo_owner.contents, 'packages-trunk'))31 ... evo_owner.contents, 'packages-trunk'))
32 Distribution Distribution series Source package Version32 Distribution Distribution series Source package Version
33 Ubuntu Warty (4.10) evolution33 Ubuntu Warty (4.10) evolution Remove...
34 Ubuntu Hoary (5.04) evolution 1.034 Ubuntu Hoary (5.04) evolution 1.0 Remove...
3535
36 >>> evo_owner.getLink(url='/ubuntu/hoary/+source/evolution') is not None36 >>> evo_owner.getLink(url='/ubuntu/hoary/+source/evolution') is not None
37 True37 True
@@ -54,16 +54,19 @@
5454
55Packaging links can be deleted if they were created in error.55Packaging links can be deleted if they were created in error.
5656
57 >>> form = evo_owner.getForm("delete-warty-evolution-trunk")57 >>> evo_owner.getLink(
58 >>> form.getControl(name="field.actions.delete_packaging").click()58 ... url='/ubuntu/warty/+source/evolution/+remove-packaging').click()
59 >>> print evo_owner.title59 >>> print evo_owner.title
60 Linked packages ...60 Unlink an upstream project...
61 >>> evo_owner.getControl('Unlink').click()
62 >>> print evo_owner.title
63 Linked packages...
6164
62 >>> for message in get_feedback_messages(evo_owner.contents):65 >>> for message in get_feedback_messages(evo_owner.contents):
63 ... print message66 ... print message
64 Removed upstream association between Evolution trunk and Warty.67 Removed upstream association between Evolution trunk series and Warty.
6568
66 >>> print extract_text(find_tag_by_id(69 >>> print extract_text(find_tag_by_id(
67 ... evo_owner.contents, 'packages-trunk'))70 ... evo_owner.contents, 'packages-trunk'))
68 Distribution Distribution series Source package Version71 Distribution Distribution series Source package Version
69 Ubuntu Hoary (5.04) evolution 1.072 Ubuntu Hoary (5.04) evolution 1.0 Remove...
7073
=== modified file 'lib/lp/registry/stories/productseries/xx-productseries-delete.txt'
--- lib/lp/registry/stories/productseries/xx-productseries-delete.txt 2009-12-14 13:49:03 +0000
+++ lib/lp/registry/stories/productseries/xx-productseries-delete.txt 2010-03-06 06:27:33 +0000
@@ -46,8 +46,10 @@
46 Mozilla Firefox in Launchpad46 Mozilla Firefox in Launchpad
4747
48 >>> owner_browser.getLink('All packages').click()48 >>> owner_browser.getLink('All packages').click()
49 >>> owner_browser.getControl(49 >>> link = owner_browser.getLink(
50 ... name='field.actions.delete_packaging', index=1).click()50 ... url='/ubuntu/warty/+source/mozilla-firefox/+remove-packaging')
51 >>> link.click()
52 >>> owner_browser.getControl('Unlink').click()
5153
52Then he returns to delete trunk. He is informed that deletion is permanent,54Then he returns to delete trunk. He is informed that deletion is permanent,
53and that the milestones, releases, and files will also be deleted. The55and that the milestones, releases, and files will also be deleted. The
5456
=== modified file 'lib/lp/registry/templates/distributionsourcepackage-index.pt'
--- lib/lp/registry/templates/distributionsourcepackage-index.pt 2010-02-09 15:38:13 +0000
+++ lib/lp/registry/templates/distributionsourcepackage-index.pt 2010-03-06 06:27:33 +0000
@@ -28,21 +28,6 @@
28</tal:side>28</tal:side>
2929
30<tal:main metal:fill-slot="main">30<tal:main metal:fill-slot="main">
31 <p class="error message"
32 tal:condition="view/errors"
33 tal:content="view/error_count" />
34 <p class="error message"
35 tal:repeat="form_wide_error view/form_wide_errors"
36 tal:content="structure form_wide_error">
37 Schema validation errors.
38 </p>
39 <p class="error message"
40 tal:define="error python:view.getFieldError('packaging')"
41 tal:condition="error"
42 tal:content="structure error">
43 Field specific error for the hidden packaging field.
44 </p>
45
46 <div class="top-portlet" id="bugs-and-questions-summary"31 <div class="top-portlet" id="bugs-and-questions-summary"
47 tal:define="newbugs context/new_bugtasks/count;32 tal:define="newbugs context/new_bugtasks/count;
48 open_questions view/open_questions/count">33 open_questions view/open_questions/count">
@@ -141,25 +126,14 @@
141 <a tal:attributes="href row/series_package/fmt:url"126 <a tal:attributes="href row/series_package/fmt:url"
142 tal:content="row/distroseries/title"/>127 tal:content="row/distroseries/title"/>
143 (<span tal:replace="row/distroseries/status/title/lower"/>)128 (<span tal:replace="row/distroseries/status/title/lower"/>)
144 <div style="float:right;">129 <div style="float:right; white-space: nowrap">
145 <a tal:condition="not: row/packaging"130 <a tal:condition="not: row/packaging"
146 tal:replace="structure row/series_package/menu:overview/set_upstream/fmt:link"/>131 tal:replace="structure row/series_package/menu:overview/set_upstream/fmt:link"/>
147 <tal:has_packaging condition="row/packaging">132 <tal:has_packaging condition="row/packaging">
148 <img tal:replace="structure row/packaging/productseries/image:icon"/>133 <img tal:replace="structure row/packaging/productseries/image:icon"/>
149 <a tal:replace="structure row/packaging/productseries/fmt:link"/>134 <a tal:replace="structure row/packaging/productseries/fmt:link"/>
150 <form style="display: inline"135 <a tal:replace="structure row/series_package/menu:overview/edit_packaging/fmt:icon"/>
151 method="POST"136 <a tal:replace="structure row/series_package/menu:overview/remove_packaging/fmt:icon" />
152 tal:condition="row/hidden_packaging_field"
153 tal:attributes="action request/URL;
154 id row/delete_packaging_form_id">
155 <tal:hidden replace="structure row/hidden_packaging_field" />
156 <tal:action replace="structure view/renderDeletePackagingAction" />
157 </form>
158 <a tal:attributes="
159 href row/series_package/menu:overview/edit_packaging/url;
160 title string:Edit upsteam link">
161 <img src="/@@/edit"/>
162 </a>
163 </tal:has_packaging>137 </tal:has_packaging>
164 </div><!--float right-->138 </div><!--float right-->
165 </td>139 </td>
166140
=== modified file 'lib/lp/registry/templates/distroseries-packaging.pt'
--- lib/lp/registry/templates/distroseries-packaging.pt 2010-02-16 19:07:42 +0000
+++ lib/lp/registry/templates/distroseries-packaging.pt 2010-03-06 06:27:33 +0000
@@ -18,7 +18,7 @@
18 <p>18 <p>
19 The packages are listed by priority based on the greatest need to19 The packages are listed by priority based on the greatest need to
20 forward bugs, sync translations, or specify the latest20 forward bugs, sync translations, or specify the latest
21 development branch. 21 development branch.
22 </p>22 </p>
2323
24 <ul id="related-pages" class="horizontal">24 <ul id="related-pages" class="horizontal">
@@ -72,42 +72,7 @@
72 tal:condition="product/summary"72 tal:condition="product/summary"
73 tal:content="product/summary"/>73 tal:content="product/summary"/>
74 </td>74 </td>
7575 <td tal:content="structure package/@@+upstream-connections"/>
76 <td>
77 <dl>
78 <dt>
79 <a tal:replace="structure product/fmt:link" /> project:
80 </dt>
81 <dd title="Project bug supervisor">
82 Bug supervisor:
83 <tal:yes-no
84 replace="structure product/bug_supervisor/image:boolean" />
85 </dd>
86 <dd title="Project bug tracker"
87 tal:define="bool product/project|bugtracker|product/bugtracker|product/official_malone">
88 Bug tracker:
89 <tal:yes-no replace="structure bool/image:boolean"/>
90 </dd>
91 </dl>
92 </td>
93
94 <td>
95 <dl>
96 <dt style="margin-bottom: 6px;">
97 <a tal:replace="structure series/fmt:link" />:
98 </dt>
99 <dd title="Series branch">
100 Branch:
101 <tal:yes-no replace="structure series/branch/image:boolean"/>
102 </dd>
103 <dd title="Series translations auto import"
104 tal:condition="translations"
105 tal:define="bool not:series/translations_autoimport_mode/enumvalue:NO_IMPORT">
106 Translations:
107 <tal:yes-no replace="structure bool/image:boolean"/>
108 </dd>
109 </dl>
110 </td>
111 </tr>76 </tr>
112 </tal:packaging>77 </tal:packaging>
113 </tbody>78 </tbody>
11479
=== modified file 'lib/lp/registry/templates/product-packages.pt'
--- lib/lp/registry/templates/product-packages.pt 2010-01-11 20:58:42 +0000
+++ lib/lp/registry/templates/product-packages.pt 2010-03-06 06:27:33 +0000
@@ -40,16 +40,11 @@
40 <th>Distribution series</th>40 <th>Distribution series</th>
41 <th>Source package</th>41 <th>Source package</th>
42 <th>Version</th>42 <th>Version</th>
43 <th style="width: 1em;"43 <th>&nbsp;</th>
44 tal:condition="view/can_delete_packaging">&nbsp;</th>
45 </tr>44 </tr>
46 </thead>45 </thead>
47 <tbody>46 <tbody>
48 <tr tal:repeat="packaging_dict packagings">47 <tr tal:repeat="packaging packagings">
49 <tal:packaging_field
50 define="packaging packaging_dict/packaging;
51 form_id packaging_dict/form_id;
52 field packaging_dict/field">
53 <td>48 <td>
54 <a tal:replace="structure packaging/distroseries/distribution/fmt:link" />49 <a tal:replace="structure packaging/distroseries/distribution/fmt:link" />
55 </td>50 </td>
@@ -69,17 +64,11 @@
69 <tal:currentrelease64 <tal:currentrelease
70 replace="packaging/sourcepackage/currentrelease/version|nothing">65 replace="packaging/sourcepackage/currentrelease/version|nothing">
71 2.3.4-166 2.3.4-1
72 </tal:currentrelease> 67 </tal:currentrelease>
73 </td>68 </td>
74 <td tal:condition="view/can_delete_packaging">69 <td>
75 <form style="display: inline" method="post"70 <a tal:replace="structure packaging/sourcepackage/menu:overview/remove_packaging/fmt:icon" />
76 tal:attributes="action request/URL;71 </td>
77 id form_id">
78 <tal:hidden replace="structure field" />
79 <tal:action replace="structure view/renderDeletePackagingAction" />
80 </form>
81 </td>
82 </tal:packaging_field>
83 </tr>72 </tr>
84 </tbody>73 </tbody>
85 </table>74 </table>
@@ -99,7 +88,7 @@
99 <h2>Packages by distribution</h2>88 <h2>Packages by distribution</h2>
10089
101 <tal:distribution repeat="distro view/distro_packaging">90 <tal:distribution repeat="distro view/distro_packaging">
102 91
103 <h3>92 <h3>
104 <a tal:attributes="href distro/distribution/fmt:url"93 <a tal:attributes="href distro/distribution/fmt:url"
105 tal:content="distro/distribution/title">Ubuntu Linux</a>94 tal:content="distro/distribution/title">Ubuntu Linux</a>
10695
=== modified file 'lib/lp/registry/templates/sourcepackage-portlet-associations.pt'
--- lib/lp/registry/templates/sourcepackage-portlet-associations.pt 2010-02-16 19:13:31 +0000
+++ lib/lp/registry/templates/sourcepackage-portlet-associations.pt 2010-03-06 06:27:33 +0000
@@ -10,47 +10,7 @@
10 <div class="portletBody portletContent"10 <div class="portletBody portletContent"
11 tal:define="series context/productseries">11 tal:define="series context/productseries">
12 <tal:has_series condition="series">12 <tal:has_series condition="series">
13 <div id="upstreams" class="two-column-list">13 <div tal:content="structure context/@@+upstream-connections"/>
14 <dl>
15 <dt>
16 <a tal:replace="structure series/product/fmt:link" /> project
17 </dt>
18
19 <tal:has_pg define="project series/product/project"
20 condition="project">
21 <dd title="Project group">
22 Project Group:
23 <a tal:replace="structure project/fmt:link" />
24 </dd>
25 </tal:has_pg>
26
27 <dd title="Bug supervisor">
28 Bug supervisor:
29 <tal:yes-no replace="structure series/product/bug_supervisor/image:boolean"/>
30 </dd>
31
32 <dd title="Bug tracker">
33 Bug tracker:
34 <tal:yes-no replace="structure view/has_bugtracker/image:boolean"/>
35 </dd>
36 </dl>
37 <dl>
38 <dt>
39 <a tal:replace="structure series/fmt:link" />
40 </dt>
41 <dd title="Series branch">
42 Branch:
43 <tal:yes-no replace="structure series/branch/image:boolean"/>
44 </dd>
45 <dd title="Series translations auto import"
46 tal:condition="context/getTranslationTemplates"
47 tal:define="bool not:series/translations_autoimport_mode/enumvalue:NO_IMPORT">
48 Translations:
49 <tal:yes-no replace="structure bool/image:boolean"/>
50 </dd>
51 </dl>
52
53 </div>
5414
55 <ul class="horizontal">15 <ul class="horizontal">
56 <li>16 <li>
@@ -58,12 +18,6 @@
58 tal:attributes="href series/product/menu:overview/packages/fmt:url"18 tal:attributes="href series/product/menu:overview/packages/fmt:url"
59 >Show upstream links</a>19 >Show upstream links</a>
60 </li>20 </li>
61 <li>
62 <a class="sprite edit"
63 tal:attributes="
64 href context/menu:overview/edit_packaging/fmt:url"
65 >Change upstream project and series</a>
66 </li>
67 </ul>21 </ul>
68 </tal:has_series>22 </tal:has_series>
6923
7024
=== added file 'lib/lp/registry/templates/sourcepackage-remove-packaging.pt'
--- lib/lp/registry/templates/sourcepackage-remove-packaging.pt 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/templates/sourcepackage-remove-packaging.pt 2010-03-06 06:27:33 +0000
@@ -0,0 +1,34 @@
1<html
2 xmlns="http://www.w3.org/1999/xhtml"
3 xmlns:tal="http://xml.zope.org/namespaces/tal"
4 xmlns:metal="http://xml.zope.org/namespaces/metal"
5 metal:use-macro="view/macro:page/main_only"
6>
7
8<body>
9
10<div metal:fill-slot="main">
11
12 <div tal:condition="not: context/productseries"
13 class="error">
14 This upstream association was deleted already.
15 </div>
16 <div tal:condition="context/productseries">
17 <div metal:use-macro="context/@@launchpad_form/form">
18 <div metal:fill-slot="extra_info">
19 <p>
20 Do you want to remove the upstream link to
21 <a tal:replace="structure context/productseries/fmt:link"/>?
22 </p>
23 <p>
24 Links from distribution packages to upstream project series let
25 distribution and upstream maintainers share bugs, patches, and
26 translations efficiently.
27 </p>
28 </div>
29 </div>
30 </div>
31
32</div>
33</body>
34</html>
035
=== added file 'lib/lp/registry/templates/sourcepackage-upstream-connections.pt'
--- lib/lp/registry/templates/sourcepackage-upstream-connections.pt 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/templates/sourcepackage-upstream-connections.pt 2010-03-06 06:27:33 +0000
@@ -0,0 +1,63 @@
1<tal:root
2 xmlns:tal="http://xml.zope.org/namespaces/tal"
3 xmlns:metal="http://xml.zope.org/namespaces/metal"
4 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
5 omit-tag="">
6
7<tal:comment condition="nothing">
8 view.request.annotations['show_edit_buttons'] can be set in views
9 that include this page to display the edit and remove buttons.
10</tal:comment>
11<div id="upstreams" tal:define="series context/productseries">
12 <dl>
13 <dd>
14 <tal:has_pg condition="series/product/project">
15 <a tal:replace="structure series/product/project/fmt:link" /> &rArr;
16 </tal:has_pg>
17 <a tal:replace="structure series/product/fmt:link" /> &rArr;
18 <a tal:content="series/name"
19 tal:attributes="href series/fmt:url" />
20 <span tal:condition="request/annotations/show_edit_buttons | nothing">
21 <a tal:replace="
22 structure context/menu:overview/edit_packaging/fmt:icon" />
23 <a tal:replace="
24 structure context/menu:overview/remove_packaging/fmt:icon " />
25 </span>
26 </dd>
27 </dl>
28
29 <style>
30 #upstream-fields dd {
31 margin: 0;
32 padding: 0;
33 }
34 </style>
35 <div id="upstream-fields" class="two-column-list">
36 <dl>
37 <dd title="Bug supervisor">
38 Bug supervisor:
39 <tal:yes-no replace="structure series/product/bug_supervisor/image:boolean"/>
40 </dd>
41
42 <dd title="Bug tracker">
43 Bug tracker:
44 <tal:yes-no replace="structure view/has_bugtracker/image:boolean"/>
45 </dd>
46 </dl>
47 <dl>
48 <dd title="Series branch">
49 Branch:
50 <tal:yes-no replace="structure series/branch/image:boolean"/>
51 </dd>
52 <dd title="Series translations auto import"
53 tal:condition="context/getTranslationTemplates"
54 tal:define="bool not:series/translations_autoimport_mode/enumvalue:NO_IMPORT">
55 Translations:
56 <tal:yes-no replace="structure bool/image:boolean"/>
57 </dd>
58 </dl>
59
60 </div>
61</div>
62
63</tal:root>
064
=== modified file 'lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt'
--- lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt 2010-02-15 10:40:56 +0000
+++ lib/lp/soyuz/stories/soyuz/xx-distroseries-sources.txt 2010-03-06 06:27:33 +0000
@@ -72,11 +72,9 @@
72Product, Branches, and Bugs:72Product, Branches, and Bugs:
7373
74 >>> print extract_text(find_tag_by_id(browser.contents, 'upstreams'))74 >>> print extract_text(find_tag_by_id(browser.contents, 'upstreams'))
75 Mozilla Firefox ... project75 the Mozilla Project...Mozilla Firefox...trunk...
76 Project Group: the Mozilla Project
77 Bug supervisor: no76 Bug supervisor: no
78 Bug tracker: yes77 Bug tracker: yes
79 Mozilla Firefox trunk series
80 Branch: no78 Branch: no
8179
82The user can also download the files for the "currentrelease" (last80The user can also download the files for the "currentrelease" (last