Merge lp:~sinzui/launchpad/series-branch-target into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Aaron Bentley
Approved revision: no longer in the source branch.
Merged at revision: not available
Proposed branch: lp:~sinzui/launchpad/series-branch-target
Merge into: lp:launchpad
Diff against target: 616 lines (+142/-153)
14 files modified
lib/lp/code/browser/tests/test_branch.py (+0/-6)
lib/lp/code/configure.zcml (+0/-4)
lib/lp/code/model/branchtarget.py (+0/-18)
lib/lp/code/model/tests/test_branchtarget.py (+1/-26)
lib/lp/code/stories/branches/xx-creating-branches.txt (+1/-1)
lib/lp/registry/browser/configure.zcml (+26/-14)
lib/lp/registry/browser/pillar.py (+1/-55)
lib/lp/registry/browser/product.py (+34/-1)
lib/lp/registry/browser/productseries.py (+48/-11)
lib/lp/registry/browser/tests/pillar-views.txt (+0/-10)
lib/lp/registry/browser/tests/productseries-views.txt (+28/-5)
lib/lp/registry/model/distribution.py (+1/-0)
lib/lp/registry/templates/productseries-codesummary.pt (+1/-1)
lib/lp/registry/templates/productseries-linkbranch.pt (+1/-1)
To merge this branch: bzr merge lp:~sinzui/launchpad/series-branch-target
Reviewer Review Type Date Requested Status
Curtis Hovey (community) Abstain
Aaron Bentley (community) Approve
Tim Penhey (community) Approve
Review via email: mp+23056@code.launchpad.net

Description of the change

See the revised merge summary below

To post a comment you must log in.
Revision history for this message
Tim Penhey (thumper) wrote :

Looks good

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

Hi Tim.

There was a single test failure that showed that this change toppled all the facet/navigation links for product series. Both classes of link reply on IPrimaryContext. This change made all productseries application and involvement links go to the product.

I am reimplementing this by removing the IBranchTarget adapter. My fix is to subclass the the InvolvementMenu and view for IProductSeries. This is not as simple as it seems. Edwin did this recently and discovered that we could not import anything from browser.pillar without causing insane adaptions; he had to place product related classes in the pillar modile. My implementaiton solves this by removing the inline python providesAdapter() and using ZCML--damn it, ZCML is useful :(

Since Edwin's branch is landing, I will wait for his branch to be merged, and I will fix his classes. I will seek a re-review on Monday I think.

review: Needs Fixing
Revision history for this message
Curtis Hovey (sinzui) wrote :
Download full text (3.6 KiB)

This is my branch to remove ProductSeriesBranchTarget.

    lp:~sinzui/launchpad/series-branch-target
    Diff size: 500
    Launchpad bug: https://bugs.launchpad.net/bugs/558307
    Test command: ./bin/test -vv \
        -t test_branchtarget \
        -t pillar-views \
        -t productseries-views
    Pre-implementation: thumper, edwin
    Target release: 10.04

Remove ProductSeriesBranchTarget
--------------------------------

Thumper says that ProductSeriesBranchTarget shouldn't exist. It does not
follow the BranchTarget model and leads to confusion, and wasted effort
trying to understand it.

The suggested change of using a function to return ProductBranchTarget
broke the facet menus (Application/tab menus) because the IPrimaryContext
for IProductSeries was wrongly changed to IProduct.

The correct fix is to remove the branch adaption and fixing the link in an
adaption InvolvedMenu. This requires fixing the adaption insanity that
happens when lp.registry.browser.pillar is imported into another module.

Rules
-----

    * Remove the ProductBranchTarget subclass with a function that returns
      ProductBranchTarget.
    * Subclass the InvolvedMenu to provide the correct link for ProductSeries.
    * Fix the adaption issues caused by importing browser.pillar.
    * Moved the Product and ProductSeries views in pillar to their proper
      modules.

QA
--

    * Visit https://edge.launchpad.net/gdp/trunk
    * Choose Submit code from the Involvement menu
    * Verify that you see the Register a branch on Gedit Developer Plugins
      page (not an oops.)

Lint
----

Linting changed files:
    * lib/lp/code/configure.zcml
    * lib/lp/code/model/branchtarget.py
    * lib/lp/code/model/tests/test_branchtarget.py
    * lib/lp/registry/browser/configure.zcml
    * lib/lp/registry/browser/pillar.py
    * lib/lp/registry/browser/product.py
    * lib/lp/registry/browser/productseries.py
    * lib/lp/registry/browser/tests/pillar-views.txt
    * lib/lp/registry/browser/tests/productseries-views.txt

Test
----

    * lib/lp/code/model/tests/test_branchtarget.py
      * Removed test for removed code.
    * lib/lp/registry/browser/tests/pillar-views.txt
      * Removed product series test because there is a better test.
    * lib/lp/registry/browser/tests/productseries-views.txt
      * Added a test to verify all the productseries links are to the
        correct host and target.

Implementation
--------------

    * lib/lp/code/configure.zcml
      * Unregistered removed code.
    * lib/lp/code/model/branchtarget.py
      * Removed the adapter.
    * lib/lp/registry/browser/configure.zcml
      * Moved the InvolvedMenu provideAdapter registration into the zcml to
        insanity when pillar is imported.
      * Moved the Product and ProductSeries registrations to their proper
        location in the ZCML file.
      * Registered a new adapter for ProductSeriesInvolvedMenu.
    * lib/lp/registry/browser/pillar.py
      * Removed the provideAdapter call that causes alternate registrations
        when browser.pillar is imported. Used ZCML to do the registration.
      * Moved the Product and ProductSeries views to their proper modules.
    * lib/...

Read more...

Revision history for this message
Aaron Bentley (abentley) :
review: Approve
Revision history for this message
Curtis Hovey (sinzui) :
review: Abstain

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/tests/test_branch.py'
2--- lib/lp/code/browser/tests/test_branch.py 2010-02-22 01:13:15 +0000
3+++ lib/lp/code/browser/tests/test_branch.py 2010-04-13 04:18:28 +0000
4@@ -291,12 +291,6 @@
5 add_view = self.get_view(product)
6 self.assertTrue(IBranchTarget.providedBy(add_view.target))
7
8- def test_target_productseries(self):
9- product = self.factory.makeProduct()
10- series = self.factory.makeProductSeries(product=product)
11- add_view = self.get_view(series)
12- self.assertTrue(IBranchTarget.providedBy(add_view.target))
13-
14
15 class TestBranchReviewerEditView(TestCaseWithFactory):
16 """Test the BranchReviewerEditView view."""
17
18=== modified file 'lib/lp/code/configure.zcml'
19--- lib/lp/code/configure.zcml 2010-04-09 02:08:39 +0000
20+++ lib/lp/code/configure.zcml 2010-04-13 04:18:28 +0000
21@@ -674,10 +674,6 @@
22 provides="lp.code.interfaces.branchtarget.IBranchTarget"
23 factory="lp.code.model.branchtarget.ProductBranchTarget"/>
24 <adapter
25- for="lp.registry.interfaces.product.IProductSeries"
26- provides="lp.code.interfaces.branchtarget.IBranchTarget"
27- factory="lp.code.model.branchtarget.ProductSeriesBranchTarget"/>
28- <adapter
29 for="lp.code.interfaces.branchtarget.IBranchTarget"
30 provides="canonical.launchpad.webapp.interfaces.ICanonicalUrlData"
31 factory="lp.code.model.branchtarget.get_canonical_url_data_for_target"/>
32
33=== modified file 'lib/lp/code/model/branchtarget.py'
34--- lib/lp/code/model/branchtarget.py 2010-04-09 02:08:39 +0000
35+++ lib/lp/code/model/branchtarget.py 2010-04-13 04:18:28 +0000
36@@ -9,7 +9,6 @@
37 'PackageBranchTarget',
38 'PersonBranchTarget',
39 'ProductBranchTarget',
40- 'ProductSeriesBranchTarget',
41 ]
42
43 from zope.component import getUtility
44@@ -337,23 +336,6 @@
45 branch.sourcepackagename = None
46
47
48-class ProductSeriesBranchTarget(ProductBranchTarget):
49-
50- def __init__(self, productseries):
51- self.productseries = productseries
52- self.product = productseries.product
53-
54- @property
55- def context(self):
56- """See `IBranchTarget`."""
57- return self.productseries
58-
59- @property
60- def supports_code_imports(self):
61- """See `IBranchTarget`."""
62- return False
63-
64-
65 def get_canonical_url_data_for_target(branch_target):
66 """Return the `ICanonicalUrlData` for an `IBranchTarget`."""
67 return ICanonicalUrlData(branch_target.context)
68
69=== modified file 'lib/lp/code/model/tests/test_branchtarget.py'
70--- lib/lp/code/model/tests/test_branchtarget.py 2010-04-09 02:08:39 +0000
71+++ lib/lp/code/model/tests/test_branchtarget.py 2010-04-13 04:18:28 +0000
72@@ -12,8 +12,7 @@
73
74 from lp.code.model.branchtarget import (
75 check_default_stacked_on,
76- PackageBranchTarget, PersonBranchTarget, ProductBranchTarget,
77- ProductSeriesBranchTarget)
78+ PackageBranchTarget, PersonBranchTarget, ProductBranchTarget)
79 from lp.code.enums import BranchType, RevisionControlSystems
80 from lp.code.interfaces.branchtarget import IBranchTarget
81 from lp.code.interfaces.codeimport import ICodeImport
82@@ -415,30 +414,6 @@
83 self.assertEqual(self.target, code_import.branch.target)
84
85
86-class TestProductSeriesBranchTarget(TestCaseWithFactory):
87-
88- layer = DatabaseFunctionalLayer
89-
90- def setUp(self):
91- TestCaseWithFactory.setUp(self)
92- self.original = self.factory.makeProductSeries()
93- self.target = ProductSeriesBranchTarget(self.original)
94-
95- def test_adapter(self):
96- target = IBranchTarget(self.original)
97- self.assertIsInstance(target, ProductSeriesBranchTarget)
98-
99- def test_doesnt_support_code_imports(self):
100- self.assertFalse(self.target.supports_code_imports)
101-
102- def test_creating_code_import_fails(self):
103- self.assertRaises(
104- AssertionError, self.target.newCodeImport,
105- self.factory.makePerson(),
106- self.factory.getUniqueString("name-"),
107- RevisionControlSystems.GIT, url=self.factory.getUniqueURL())
108-
109-
110 class TestCheckDefaultStackedOnBranch(TestCaseWithFactory):
111 """Only certain branches are allowed to be default stacked-on branches."""
112
113
114=== modified file 'lib/lp/code/stories/branches/xx-creating-branches.txt'
115--- lib/lp/code/stories/branches/xx-creating-branches.txt 2009-11-13 21:28:28 +0000
116+++ lib/lp/code/stories/branches/xx-creating-branches.txt 2010-04-13 04:18:28 +0000
117@@ -175,7 +175,7 @@
118 >>> browser.open('http://launchpad.dev/gnome-terminal/trunk')
119 >>> browser.getLink('registering a mirrored branch').click()
120 >>> print browser.title
121- Register a branch : Series trunk : GNOME Terminal
122+ Register a branch : GNOME Terminal
123
124 The user sees that he is registering a branch for the series' project.
125
126
127=== modified file 'lib/lp/registry/browser/configure.zcml'
128--- lib/lp/registry/browser/configure.zcml 2010-04-08 19:19:13 +0000
129+++ lib/lp/registry/browser/configure.zcml 2010-04-13 04:18:28 +0000
130@@ -483,6 +483,12 @@
131 for="lp.registry.interfaces.pillar.IPillarNameSet"
132 path_expression="string:pillars"
133 parent_utility="canonical.launchpad.interfaces.ILaunchpadRoot"/>
134+ <adapter
135+ provides="canonical.launchpad.webapp.interfaces.INavigationMenu"
136+ for="lp.registry.browser.pillar.IInvolved"
137+ factory="lp.registry.browser.pillar.InvolvedMenu"
138+ name="overview"
139+ permission="zope.Public"/>
140 <browser:page
141 for="lp.registry.interfaces.pillar.IPillar"
142 name="+listing-simple"
143@@ -495,20 +501,6 @@
144 facet="overview"
145 permission="zope.Public"
146 template="../templates/pillar-involvement-portlet.pt"/>
147- <browser:page
148- name="+get-involved"
149- for="lp.registry.interfaces.product.IProduct"
150- class="lp.registry.browser.pillar.ProductInvolvementView"
151- facet="overview"
152- permission="zope.Public"
153- template="../templates/pillar-involvement-portlet.pt"/>
154- <browser:page
155- name="+get-involved"
156- for="lp.registry.interfaces.product.IProductSeries"
157- class="lp.registry.browser.pillar.ProductSeriesInvolvementView"
158- facet="overview"
159- permission="zope.Public"
160- template="../templates/pillar-involvement-portlet.pt"/>
161 <facet
162 facet="overview">
163 <browser:url
164@@ -1384,6 +1376,13 @@
165 path_expression="name"
166 parent_utility="canonical.launchpad.interfaces.ILaunchpadRoot"/>
167 <browser:page
168+ name="+get-involved"
169+ for="lp.registry.interfaces.product.IProduct"
170+ class="lp.registry.browser.product.ProductInvolvementView"
171+ facet="overview"
172+ permission="zope.Public"
173+ template="../templates/pillar-involvement-portlet.pt"/>
174+ <browser:page
175 for="lp.registry.interfaces.product.IProduct"
176 name="+download"
177 class="lp.registry.browser.product.ProductDownloadFilesView"
178@@ -1600,6 +1599,12 @@
179 for="lp.registry.interfaces.productseries.IProductSeries"
180 path_expression="name"
181 attribute_to_parent="product"/>
182+ <adapter
183+ provides="canonical.launchpad.webapp.interfaces.INavigationMenu"
184+ for="lp.registry.browser.productseries.IProductSeriesInvolved"
185+ factory="lp.registry.browser.productseries.ProductSeriesInvolvedMenu"
186+ name="overview"
187+ permission="zope.Public"/>
188 <browser:defaultView
189 for="lp.registry.interfaces.productseries.IProductSeries"
190 name="+index"/>
191@@ -1611,6 +1616,13 @@
192 for="lp.registry.interfaces.productseries.IProductSeries"
193 layer="canonical.launchpad.layers.BugsLayer"
194 name="+bugs-index"/>
195+ <browser:page
196+ name="+get-involved"
197+ for="lp.registry.interfaces.productseries.IProductSeries"
198+ class="lp.registry.browser.productseries.ProductSeriesInvolvementView"
199+ facet="overview"
200+ permission="zope.Public"
201+ template="../templates/pillar-involvement-portlet.pt"/>
202 <browser:pages
203 for="lp.registry.interfaces.productseries.IProductSeries"
204 facet="overview"
205
206=== modified file 'lib/lp/registry/browser/pillar.py'
207--- lib/lp/registry/browser/pillar.py 2010-04-09 18:19:54 +0000
208+++ lib/lp/registry/browser/pillar.py 2010-04-13 04:18:28 +0000
209@@ -6,17 +6,16 @@
210 __metaclass__ = type
211
212 __all__ = [
213+ 'InvolvedMenu',
214 'PillarView',
215 ]
216
217
218 from operator import attrgetter
219
220-from zope.component.globalregistry import provideAdapter
221 from zope.interface import implements, Interface
222
223 from canonical.cachedproperty import cachedproperty
224-from canonical.launchpad.webapp.interfaces import INavigationMenu
225 from canonical.launchpad.webapp.menu import Link, NavigationMenu
226 from canonical.launchpad.webapp.publisher import LaunchpadView, nearest
227 from canonical.launchpad.webapp.tales import MenuAPI
228@@ -146,56 +145,3 @@
229 return sorted([
230 link for link in important_links if not link.enabled],
231 key=attrgetter('sort_key'))
232-
233-
234-provideAdapter(
235- InvolvedMenu, [IInvolved], INavigationMenu, name="overview")
236-
237-
238-# This class can't be moved into the browser/product.py file, since
239-# the pillar-views.txt test will fail due to the MenuAPI adapter
240-# for PillarView.enabled_links not working.
241-class ProductInvolvementView(PillarView):
242- """Encourage configuration of involvement links for projects."""
243-
244- has_involvement = True
245- visible_disabled_link_names = ['submit_code']
246-
247- @property
248- def configuration_links(self):
249- """The enabled involvement links."""
250- overview_menu = MenuAPI(self.context).overview
251- series_menu = MenuAPI(self.context.development_focus).overview
252- configuration_names = [
253- 'configure_answers',
254- 'configure_bugtracker',
255- 'configure_translations',
256- ]
257- configuration_links = [
258- overview_menu[name] for name in configuration_names]
259- set_branch = series_menu['set_branch']
260- set_branch.text = 'Configure project branch'
261- configuration_links.append(set_branch)
262- return sorted([
263- link for link in configuration_links if link.enabled],
264- key=attrgetter('sort_key'))
265-
266-
267-class ProductSeriesInvolvementView(PillarView):
268- """Encourage configuration of involvement links for project series."""
269-
270- has_involvement = True
271- visible_disabled_link_names = ['submit_code']
272-
273- def __init__(self, context, request):
274- super(ProductSeriesInvolvementView, self).__init__(context, request)
275- self.official_codehosting = self.context.branch is not None
276- self.official_answers = False
277-
278- @property
279- def configuration_links(self):
280- """The enabled involvement links."""
281- series_menu = MenuAPI(self.context).overview
282- set_branch = series_menu['set_branch']
283- set_branch.text = 'Configure series branch'
284- return [set_branch]
285
286=== modified file 'lib/lp/registry/browser/product.py'
287--- lib/lp/registry/browser/product.py 2010-04-08 19:19:13 +0000
288+++ lib/lp/registry/browser/product.py 2010-04-13 04:18:28 +0000
289@@ -14,7 +14,6 @@
290 'ProductBugsMenu',
291 'ProductConfigureAnswersView',
292 'ProductConfigureBlueprintsView',
293- 'ProductConfigureBranchesView',
294 'ProductConfigureBugTrackerView',
295 'ProductConfigureTranslationsView',
296 'ProductDownloadFileMixin',
297@@ -94,6 +93,7 @@
298 IRegistryCollectionNavigationMenu, RegistryCollectionActionMenuBase)
299 from lp.answers.browser.faqtarget import FAQTargetNavigationMixin
300 from canonical.launchpad.browser.feeds import FeedsMixin
301+from lp.registry.browser.pillar import PillarView
302 from lp.registry.browser.productseries import get_series_branch_error
303 from lp.translations.browser.customlanguagecode import (
304 HasCustomLanguageCodesTraversalMixin)
305@@ -115,6 +115,7 @@
306 action, custom_widget, LaunchpadEditFormView, LaunchpadFormView,
307 ReturnToReferrerMixin)
308 from canonical.launchpad.webapp.menu import NavigationMenu
309+from canonical.launchpad.webapp.tales import MenuAPI
310 from canonical.widgets.popup import PersonPickerWidget
311 from canonical.widgets.date import DateWidget
312 from canonical.widgets.itemswidgets import (
313@@ -299,6 +300,32 @@
314 return Link('', text, summary)
315
316
317+class ProductInvolvementView(PillarView):
318+ """Encourage configuration of involvement links for projects."""
319+
320+ has_involvement = True
321+ visible_disabled_link_names = ['submit_code']
322+
323+ @property
324+ def configuration_links(self):
325+ """The enabled involvement links."""
326+ overview_menu = MenuAPI(self.context).overview
327+ series_menu = MenuAPI(self.context.development_focus).overview
328+ configuration_names = [
329+ 'configure_answers',
330+ 'configure_bugtracker',
331+ 'configure_translations',
332+ ]
333+ configuration_links = [
334+ overview_menu[name] for name in configuration_names]
335+ set_branch = series_menu['set_branch']
336+ set_branch.text = 'Configure project branch'
337+ configuration_links.append(set_branch)
338+ return sorted([
339+ link for link in configuration_links if link.enabled],
340+ key=attrgetter('sort_key'))
341+
342+
343 class ProductNavigationMenu(NavigationMenu):
344
345 usedfor = IProduct
346@@ -420,6 +447,7 @@
347 'announcements',
348 'administer',
349 'review_license',
350+ 'branch_add',
351 'branchvisibility',
352 'rdf',
353 'branding',
354@@ -476,6 +504,11 @@
355 text = 'Branch Visibility Policy'
356 return Link('+branchvisibility', text, icon='edit')
357
358+ def branch_add(self):
359+ text = 'Register a branch'
360+ summary = "Register a new Bazaar branch for this project"
361+ return Link('+addbranch', text, summary, icon='add')
362+
363
364 class ProductBugsMenu(ApplicationMenu, StructuralSubscriptionMenuMixin):
365
366
367=== modified file 'lib/lp/registry/browser/productseries.py'
368--- lib/lp/registry/browser/productseries.py 2010-04-09 02:25:21 +0000
369+++ lib/lp/registry/browser/productseries.py 2010-04-13 04:18:28 +0000
370@@ -13,6 +13,7 @@
371 'ProductSeriesEditView',
372 'ProductSeriesFacets',
373 'ProductSeriesFileBugRedirect',
374+ 'ProductSeriesInvolvementView',
375 'ProductSeriesLinkBranchView',
376 'ProductSeriesLinkBranchFromCodeView',
377 'ProductSeriesNavigation',
378@@ -34,7 +35,7 @@
379 from zope.component import getUtility
380 from zope.app.form.browser import TextAreaWidget, TextWidget
381 from zope.formlib import form
382-from zope.interface import Interface
383+from zope.interface import implements, Interface
384 from zope.schema import Choice
385 from zope.schema.vocabulary import SimpleTerm, SimpleVocabulary
386
387@@ -84,11 +85,13 @@
388 action, custom_widget, LaunchpadEditFormView, LaunchpadFormView,
389 ReturnToReferrerMixin)
390 from canonical.launchpad.webapp.menu import structured
391+from canonical.launchpad.webapp.tales import MenuAPI
392 from canonical.widgets.itemswidgets import LaunchpadRadioWidget
393 from canonical.widgets.textwidgets import StrippedTextWidget
394
395 from lp.registry.browser import (
396 MilestoneOverlayMixin, RegistryDeleteViewMixin)
397+from lp.registry.browser.pillar import InvolvedMenu, PillarView
398 from lp.registry.interfaces.series import SeriesStatus
399 from lp.registry.interfaces.productseries import IProductSeries
400
401@@ -168,13 +171,53 @@
402 return Link(link, text, summary=summary)
403
404
405+class IProductSeriesInvolved(Interface):
406+ """A marker interface for getting involved."""
407+
408+
409+class ProductSeriesInvolvedMenu(InvolvedMenu):
410+ """The get involved menu."""
411+ usedfor = IProductSeriesInvolved
412+ links = [
413+ 'report_bug', 'help_translate', 'submit_code', 'register_blueprint']
414+
415+ def submit_code(self):
416+ product = self.context.context.product
417+ target = canonical_url(
418+ product, view_name='+addbranch', rootsite='code')
419+ enabled = product.official_codehosting
420+ return Link(
421+ target, 'Submit code', icon='code', enabled=enabled)
422+
423+
424+class ProductSeriesInvolvementView(PillarView):
425+ """Encourage configuration of involvement links for project series."""
426+
427+ implements(IProductSeriesInvolved)
428+ has_involvement = True
429+ visible_disabled_link_names = ['submit_code']
430+
431+ def __init__(self, context, request):
432+ super(ProductSeriesInvolvementView, self).__init__(context, request)
433+ self.official_codehosting = self.context.branch is not None
434+ self.official_answers = False
435+
436+ @property
437+ def configuration_links(self):
438+ """The enabled involvement links."""
439+ series_menu = MenuAPI(self.context).overview
440+ set_branch = series_menu['set_branch']
441+ set_branch.text = 'Configure series branch'
442+ return [set_branch]
443+
444+
445 class ProductSeriesOverviewMenu(
446 ApplicationMenu, StructuralSubscriptionMenuMixin):
447 """The overview menu."""
448 usedfor = IProductSeries
449 facet = 'overview'
450 links = [
451- 'edit', 'delete', 'driver', 'link_branch', 'branch_add', 'ubuntupkg',
452+ 'edit', 'delete', 'driver', 'link_branch', 'ubuntupkg',
453 'create_milestone', 'create_release', 'rdf', 'subscribe',
454 'set_branch',
455 ]
456@@ -228,11 +271,6 @@
457 summary = 'Change the branch for this series'
458 return Link('+setbranch', text, summary, icon=icon)
459
460- def branch_add(self):
461- text = 'Register a branch'
462- summary = "Register a new Bazaar branch for this series' project"
463- return Link('+addbranch', text, summary, icon='add')
464-
465 @enabled_with_permission('launchpad.AnyPerson')
466 def ubuntupkg(self):
467 """Return a link to link this series to an ubuntu sourcepackage."""
468@@ -507,7 +545,6 @@
469 # to this series, or any other series.
470 pass
471
472-
473 @action('Update', name='continue')
474 def continue_action(self, action, data):
475 # set the packaging record for this productseries in the current
476@@ -879,8 +916,8 @@
477 # Extend the allowed schemes for the repository URL based on
478 # rcs_type.
479 extra_schemes = {
480- RevisionControlSystemsExtended.BZR_SVN:['svn'],
481- RevisionControlSystemsExtended.GIT:['git'],
482+ RevisionControlSystemsExtended.BZR_SVN: ['svn'],
483+ RevisionControlSystemsExtended.GIT: ['git'],
484 }
485 schemes.update(extra_schemes.get(rcs_type, []))
486 return schemes
487@@ -933,7 +970,7 @@
488 @property
489 def target(self):
490 """The branch target for the context."""
491- return IBranchTarget(self.context)
492+ return IBranchTarget(self.context.product)
493
494 @action(_('Update'), name='update')
495 def update_action(self, action, data):
496
497=== modified file 'lib/lp/registry/browser/tests/pillar-views.txt'
498--- lib/lp/registry/browser/tests/pillar-views.txt 2010-04-09 02:25:21 +0000
499+++ lib/lp/registry/browser/tests/pillar-views.txt 2010-04-13 04:18:28 +0000
500@@ -132,16 +132,6 @@
501 ... print link.name
502 report_bug
503
504-ProductSeries can use this view. The product is used to set the links,
505-but the answers link will be disbaled.
506-
507- >>> series = factory.makeProductSeries(product=product)
508- >>> product.official_answers = True
509- >>> view = create_view(series, '+get-involved')
510- >>> for link in view.enabled_links:
511- ... print link.name
512- register_blueprint
513-
514 DistributionSourcePackages can use this view. The distribution is used to
515 set the links. Despite the fact that the distribution uses blueprints,
516 and translations those links are not enabled for DistributionSourcePackages.
517
518=== modified file 'lib/lp/registry/browser/tests/productseries-views.txt'
519--- lib/lp/registry/browser/tests/productseries-views.txt 2010-04-02 14:08:22 +0000
520+++ lib/lp/registry/browser/tests/productseries-views.txt 2010-04-13 04:18:28 +0000
521@@ -1,7 +1,7 @@
522 ProductSeries Views
523 ===================
524
525-Productseries menus
526+ProductSeries menus
527 -------------------
528
529 The ProductSeriesOverviewMenu provides the links to the common ProductSeries
530@@ -11,11 +11,37 @@
531 ... ProductSeriesOverviewMenu)
532 >>> from lp.testing.menu import check_menu_links
533
534- >>> series = factory.makeSeries()
535+ >>> product = factory.makeProduct(name='app')
536+ >>> series = factory.makeSeries(name='simple', product=product)
537 >>> check_menu_links(ProductSeriesOverviewMenu(series))
538 True
539
540
541+ProductSeries Involvement links
542+...............................
543+
544+The ProductSeries involvement view uses the ProductSeriesInvolvedMenu when
545+rendering links:
546+
547+ >>> from operator import attrgetter
548+ >>> from canonical.launchpad.webapp.tales import MenuAPI
549+
550+ >>> login_person(product.owner)
551+ >>> product.official_answers = True
552+ >>> product.official_blueprints = True
553+ >>> product.official_malone = True
554+ >>> product.official_rosetta = True
555+ >>> view = create_view(series, '+get-involved')
556+ >>> menuapi = MenuAPI(view)
557+ >>> for link in sorted(
558+ ... menuapi.navigation.values(), key=attrgetter('sort_key')):
559+ ... print link.url
560+ http://bugs.launchpad.dev/app/simple/+filebug
561+ http://translations.launchpad.dev/app/simple
562+ http://code.launchpad.dev/app/+addbranch
563+ http://blueprints.launchpad.dev/app/simple/+addspec
564+
565+
566 ProductSeries view
567 ------------------
568
569@@ -26,9 +52,6 @@
570 >>> from canonical.launchpad.testing.pages import find_tag_by_id
571
572 >>> login('foo.bar@canonical.com')
573-
574- >>> product = factory.makeProduct(name='app')
575- >>> series = factory.makeSeries(name='simple', product=product)
576 >>> view = create_view(series, '+index', principal=product.owner)
577 >>> script = find_tag_by_id(view.render(), 'milestone-script')
578 >>> print script
579
580=== modified file 'lib/lp/registry/model/distribution.py'
581--- lib/lp/registry/model/distribution.py 2010-04-10 09:24:41 +0000
582+++ lib/lp/registry/model/distribution.py 2010-04-13 04:18:28 +0000
583@@ -21,6 +21,7 @@
584 from lp.archivepublisher.debversion import Version
585 from canonical.cachedproperty import cachedproperty
586 from canonical.database.constants import UTC_NOW
587+
588 from canonical.database.datetimecol import UtcDateTimeCol
589 from canonical.database.enumcol import EnumCol
590 from canonical.database.sqlbase import (
591
592=== modified file 'lib/lp/registry/templates/productseries-codesummary.pt'
593--- lib/lp/registry/templates/productseries-codesummary.pt 2010-03-11 15:32:22 +0000
594+++ lib/lp/registry/templates/productseries-codesummary.pt 2010-04-13 04:18:28 +0000
595@@ -35,7 +35,7 @@
596 <ul class="bulleted" style="margin-bottom: 0;">
597 <li>
598 Have the branch mirrored from a remote location by
599- <a tal:attributes="href context/menu:overview/branch_add/fmt:url">
600+ <a tal:attributes="href context/product/menu:overview/branch_add/fmt:url">
601 registering a mirrored branch</a>
602 </li>
603 <li id="ssh-key-directions">
604
605=== modified file 'lib/lp/registry/templates/productseries-linkbranch.pt'
606--- lib/lp/registry/templates/productseries-linkbranch.pt 2010-03-11 15:26:46 +0000
607+++ lib/lp/registry/templates/productseries-linkbranch.pt 2010-04-13 04:18:28 +0000
608@@ -22,7 +22,7 @@
609 <ul class="bulleted" style="margin-bottom: 0;">
610 <li>
611 Have the branch mirrored from a remote location by
612- <a tal:attributes="href context/menu:overview/branch_add/fmt:url">
613+ <a tal:attributes="href context/product/menu:overview/branch_add/fmt:url">
614 registering a mirrored branch</a>
615 </li>
616 <li id="ssh-key-directions">