Merge lp:~cjwatson/launchpad/git-recipe-browser-listing into lp:launchpad

Proposed by Colin Watson on 2016-01-12
Status: Merged
Merged at revision: 17900
Proposed branch: lp:~cjwatson/launchpad/git-recipe-browser-listing
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/recipe-name-policy
Diff against target: 509 lines (+233/-35)
11 files modified
lib/lp/code/browser/configure.zcml (+21/-3)
lib/lp/code/browser/gitref.py (+23/-3)
lib/lp/code/browser/gitrepository.py (+24/-3)
lib/lp/code/browser/sourcepackagerecipelisting.py (+6/-4)
lib/lp/code/stories/sourcepackagerecipes/xx-recipe-listings.txt (+99/-14)
lib/lp/code/templates/branch-recipes.pt (+1/-1)
lib/lp/code/templates/gitref-index.pt (+11/-2)
lib/lp/code/templates/gitref-recipes.pt (+19/-0)
lib/lp/code/templates/gitrepository-index.pt (+7/-2)
lib/lp/code/templates/gitrepository-recipes.pt (+19/-0)
lib/lp/code/templates/sourcepackagerecipe-listing.pt (+3/-3)
To merge this branch: bzr merge lp:~cjwatson/launchpad/git-recipe-browser-listing
Reviewer Review Type Date Requested Status
William Grant code 2016-01-12 Approve on 2016-01-15
Review via email: mp+282321@code.launchpad.net

Commit message

Add views to list existing Git recipes.

Description of the change

Add views to list existing Git recipes.

To post a comment you must log in.
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/browser/configure.zcml'
2--- lib/lp/code/browser/configure.zcml 2015-11-23 11:34:15 +0000
3+++ lib/lp/code/browser/configure.zcml 2016-01-12 15:24:34 +0000
4@@ -1,4 +1,4 @@
5-<!-- Copyright 2009-2015 Canonical Ltd. This software is licensed under the
6+<!-- Copyright 2009-2016 Canonical Ltd. This software is licensed under the
7 GNU Affero General Public License version 3 (see the file LICENSE).
8 -->
9
10@@ -800,6 +800,9 @@
11 <browser:page
12 name="++repository-management"
13 template="../templates/gitrepository-management.pt"/>
14+ <browser:page
15+ name="++repository-recipes"
16+ template="../templates/gitrepository-recipes.pt"/>
17 </browser:pages>
18 <browser:page
19 for="lp.code.interfaces.gitrepository.IGitRepository"
20@@ -884,11 +887,14 @@
21 name="++ref-commits"
22 template="../templates/gitref-commits.pt"/>
23 <browser:page
24+ name="++ref-management"
25+ template="../templates/gitref-management.pt"/>
26+ <browser:page
27 name="++ref-pending-merges"
28 template="../templates/gitref-pending-merges.pt"/>
29 <browser:page
30- name="++ref-management"
31- template="../templates/gitref-management.pt"/>
32+ name="++ref-recipes"
33+ template="../templates/gitref-recipes.pt"/>
34 </browser:pages>
35 <browser:page
36 for="lp.code.interfaces.gitref.IGitRef"
37@@ -1232,6 +1238,18 @@
38 name="+recipes"
39 template="../templates/sourcepackagerecipe-listing.pt"/>
40 <browser:page
41+ for="lp.code.interfaces.gitrepository.IGitRepository"
42+ class="lp.code.browser.sourcepackagerecipelisting.BranchRecipeListingView"
43+ permission="zope.Public"
44+ name="+recipes"
45+ template="../templates/sourcepackagerecipe-listing.pt"/>
46+ <browser:page
47+ for="lp.code.interfaces.gitref.IGitRef"
48+ class="lp.code.browser.sourcepackagerecipelisting.BranchRecipeListingView"
49+ permission="zope.Public"
50+ name="+recipes"
51+ template="../templates/sourcepackagerecipe-listing.pt"/>
52+ <browser:page
53 for="lp.registry.interfaces.product.IProduct"
54 class="lp.code.browser.sourcepackagerecipelisting.ProductRecipeListingView"
55 permission="zope.Public"
56
57=== modified file 'lib/lp/code/browser/gitref.py'
58--- lib/lp/code/browser/gitref.py 2015-11-30 03:03:14 +0000
59+++ lib/lp/code/browser/gitref.py 2016-01-12 15:24:34 +0000
60@@ -1,4 +1,4 @@
61-# Copyright 2015 Canonical Ltd. This software is licensed under the
62+# Copyright 2015-2016 Canonical Ltd. This software is licensed under the
63 # GNU Affero General Public License version 3 (see the file LICENSE).
64
65 """Git reference views."""
66@@ -35,6 +35,7 @@
67 from lp.code.browser.branchmergeproposal import (
68 latest_proposals_for_each_branch,
69 )
70+from lp.code.browser.sourcepackagerecipelisting import HasRecipesMenuMixin
71 from lp.code.errors import InvalidBranchMergeProposal
72 from lp.code.interfaces.branchmergeproposal import IBranchMergeProposal
73 from lp.code.interfaces.codereviewvote import ICodeReviewVoteReference
74@@ -49,18 +50,19 @@
75 Link,
76 )
77 from lp.services.webapp.authorization import check_permission
78+from lp.services.webapp.escaping import structured
79 from lp.snappy.browser.hassnaps import (
80 HasSnapsMenuMixin,
81 HasSnapsViewMixin,
82 )
83
84
85-class GitRefContextMenu(ContextMenu, HasSnapsMenuMixin):
86+class GitRefContextMenu(ContextMenu, HasRecipesMenuMixin, HasSnapsMenuMixin):
87 """Context menu for Git references."""
88
89 usedfor = IGitRef
90 facet = 'branches'
91- links = ['create_snap', 'register_merge', 'source']
92+ links = ['create_snap', 'register_merge', 'source', 'view_recipes']
93
94 def source(self):
95 """Return a link to the branch's browsing interface."""
96@@ -144,6 +146,24 @@
97 def dependent_landing_count_text(self):
98 return self._getBranchCountText(len(self.dependent_landings))
99
100+ @property
101+ def recipes_link(self):
102+ """A link to recipes for this reference."""
103+ count = self.context.recipes.count()
104+ if count == 0:
105+ # Nothing to link to.
106+ return 'No recipes using this branch.'
107+ elif count == 1:
108+ # Link to the single recipe.
109+ return structured(
110+ '<a href="%s">1 recipe</a> using this branch.',
111+ canonical_url(self.context.recipes.one())).escapedtext
112+ else:
113+ # Link to a recipe listing.
114+ return structured(
115+ '<a href="+recipes">%s recipes</a> using this branch.',
116+ count).escapedtext
117+
118
119 class GitRefRegisterMergeProposalSchema(Interface):
120 """The schema to define the form for registering a new merge proposal."""
121
122=== modified file 'lib/lp/code/browser/gitrepository.py'
123--- lib/lp/code/browser/gitrepository.py 2015-10-07 16:14:42 +0000
124+++ lib/lp/code/browser/gitrepository.py 2016-01-12 15:24:34 +0000
125@@ -1,4 +1,4 @@
126-# Copyright 2015 Canonical Ltd. This software is licensed under the
127+# Copyright 2015-2016 Canonical Ltd. This software is licensed under the
128 # GNU Affero General Public License version 3 (see the file LICENSE).
129
130 """Git repository views."""
131@@ -52,6 +52,7 @@
132 from lp.app.vocabularies import InformationTypeVocabulary
133 from lp.app.widgets.itemswidgets import LaunchpadRadioWidgetWithDescription
134 from lp.code.browser.branch import CodeEditOwnerMixin
135+from lp.code.browser.sourcepackagerecipelisting import HasRecipesMenuMixin
136 from lp.code.browser.widgets.gitrepositorytarget import (
137 GitRepositoryTargetDisplayWidget,
138 GitRepositoryTargetWidget,
139@@ -203,12 +204,14 @@
140 return Link("+delete", text, icon="trash-icon")
141
142
143-class GitRepositoryContextMenu(ContextMenu):
144+class GitRepositoryContextMenu(ContextMenu, HasRecipesMenuMixin):
145 """Context menu for `IGitRepository`."""
146
147 usedfor = IGitRepository
148 facet = "branches"
149- links = ["add_subscriber", "source", "subscription", "visibility"]
150+ links = [
151+ "add_subscriber", "source", "subscription",
152+ "view_recipes", "visibility"]
153
154 @enabled_with_permission("launchpad.AnyPerson")
155 def subscription(self):
156@@ -291,6 +294,24 @@
157 """All branches in this repository, sorted for display."""
158 return GitRefBatchNavigator(self, self.context)
159
160+ @property
161+ def recipes_link(self):
162+ """A link to recipes for this repository."""
163+ count = self.context.recipes.count()
164+ if count == 0:
165+ # Nothing to link to.
166+ return 'No recipes using this repository.'
167+ elif count == 1:
168+ # Link to the single recipe.
169+ return structured(
170+ '<a href="%s">1 recipe</a> using this repository.',
171+ canonical_url(self.context.recipes.one())).escapedtext
172+ else:
173+ # Link to a recipe listing.
174+ return structured(
175+ '<a href="+recipes">%s recipes</a> using this repository.',
176+ count).escapedtext
177+
178
179 class GitRepositoryEditFormView(LaunchpadEditFormView):
180 """Base class for forms that edit a Git repository."""
181
182=== modified file 'lib/lp/code/browser/sourcepackagerecipelisting.py'
183--- lib/lp/code/browser/sourcepackagerecipelisting.py 2015-10-05 13:36:06 +0000
184+++ lib/lp/code/browser/sourcepackagerecipelisting.py 2016-01-12 15:24:34 +0000
185@@ -1,4 +1,4 @@
186-# Copyright 2010-2015 Canonical Ltd. This software is licensed under the
187+# Copyright 2010-2016 Canonical Ltd. This software is licensed under the
188 # GNU Affero General Public License version 3 (see the file LICENSE).
189
190 """Base class view for sourcepackagerecipe listings."""
191@@ -14,6 +14,7 @@
192
193
194 from lp.code.browser.decorations import DecoratedBranch
195+from lp.code.interfaces.branch import IBranch
196 from lp.services.feeds.browser import FeedsMixin
197 from lp.services.webapp import (
198 LaunchpadView,
199@@ -42,8 +43,8 @@
200
201 @property
202 def page_title(self):
203- return 'Source Package Recipes for %(displayname)s' % {
204- 'displayname': self.context.displayname}
205+ return 'Source Package Recipes for %(display_name)s' % {
206+ 'display_name': self.context.display_name}
207
208
209 class BranchRecipeListingView(RecipeListingView):
210@@ -54,7 +55,8 @@
211 super(BranchRecipeListingView, self).initialize()
212 # Replace our context with a decorated branch, if it is not already
213 # decorated.
214- if not isinstance(self.context, DecoratedBranch):
215+ if (IBranch.providedBy(self.context) and
216+ not isinstance(self.context, DecoratedBranch)):
217 self.context = DecoratedBranch(self.context)
218
219
220
221=== modified file 'lib/lp/code/stories/sourcepackagerecipes/xx-recipe-listings.txt'
222--- lib/lp/code/stories/sourcepackagerecipes/xx-recipe-listings.txt 2011-06-29 16:49:05 +0000
223+++ lib/lp/code/stories/sourcepackagerecipes/xx-recipe-listings.txt 2016-01-12 15:24:34 +0000
224@@ -52,18 +52,84 @@
225 >>> print nopriv_browser.url
226 http://code.launchpad.dev/%7Eperson-name.../product-name.../branch.../+recipes
227
228-The "Base branch" column should not be shown.
229-
230- >>> print_recipe_listing_head(nopriv_browser)
231- Name
232- Owner
233- Registered
234-
235-The branch page should have a list of all the recipes the branch is a
236-base_branch for.
237-
238- >>> print_recipe_listing_contents(nopriv_browser)
239- spr-name... Person-name...
240+The "Base Source" column should not be shown.
241+
242+ >>> print_recipe_listing_head(nopriv_browser)
243+ Name
244+ Owner
245+ Registered
246+
247+The recipe listing page should have a list of all the recipes the branch is
248+a base for.
249+
250+ >>> print_recipe_listing_contents(nopriv_browser)
251+ spr-name... Person-name...
252+ spr-name... Person-name...
253+ spr-name... Person-name...
254+
255+
256+Git Recipe Listings
257+===================
258+
259+Create a new sample repository, some branches in it, and some source package
260+recipes to go along with them.
261+
262+ >>> login('foo.bar@canonical.com')
263+ >>> repository = factory.makeGitRepository()
264+ >>> ref1, ref2, ref3 = factory.makeGitRefs(
265+ ... repository=repository,
266+ ... paths=[u"refs/heads/a", u"refs/heads/b", u"refs/heads/c"])
267+ >>> recipe1a = factory.makeSourcePackageRecipe(branches=[ref1])
268+ >>> recipe1b = factory.makeSourcePackageRecipe(branches=[ref1])
269+ >>> recipe2 = factory.makeSourcePackageRecipe(branches=[ref2])
270+ >>> recipe3 = factory.makeSourcePackageRecipe(branches=[ref3])
271+
272+Keep these urls, including the target url. We'll use these later.
273+
274+ >>> repository_url = canonical_url(repository)
275+ >>> ref1_url = canonical_url(ref1)
276+ >>> target_url = canonical_url(repository.target)
277+
278+ >>> logout()
279+
280+Since there are 4 recipes associated with this repository now, the link
281+should now read "4 recipes." Let's click through.
282+
283+ >>> nopriv_browser.open(repository_url)
284+ >>> nopriv_browser.getLink('4 recipes').click()
285+ >>> print nopriv_browser.url
286+ http://code.launchpad.dev/%7Eperson-name.../product-name.../+git/gitrepository.../+recipes
287+
288+The "Base Source" column should not be shown.
289+
290+ >>> print_recipe_listing_head(nopriv_browser)
291+ Name
292+ Owner
293+ Registered
294+
295+The recipe listing page should have a list of all the recipes the repository
296+is a base for.
297+
298+ >>> print_recipe_listing_contents(nopriv_browser)
299+ spr-name... Person-name...
300+ spr-name... Person-name...
301+ spr-name... Person-name...
302+ spr-name... Person-name...
303+
304+If we start from one of the branches instead, then only two recipes are
305+listed.
306+
307+ >>> nopriv_browser.open(ref1_url)
308+ >>> nopriv_browser.getLink('2 recipes').click()
309+ >>> print nopriv_browser.url
310+ http://code.launchpad.dev/%7Eperson-name.../product-name.../+git/gitrepository.../+ref/a/+recipes
311+
312+ >>> print_recipe_listing_head(nopriv_browser)
313+ Name
314+ Owner
315+ Registered
316+
317+ >>> print_recipe_listing_contents(nopriv_browser)
318 spr-name... Person-name...
319 spr-name... Person-name...
320
321@@ -81,7 +147,7 @@
322 >>> print_recipe_listing_head(nopriv_browser)
323 Name
324 Owner
325- Base Branch
326+ Base Source
327 Registered
328
329 The listings should now show all recipes whose base branch is a branch from
330@@ -92,6 +158,25 @@
331 spr-name... Person-name... lp://dev/... ...
332 spr-name... Person-name... lp://dev/... ...
333
334+The same thing works for the target of the former Git repository test.
335+
336+ >>> nopriv_browser.open(target_url)
337+ >>> nopriv_browser.getLink('View source package recipes').click()
338+ >>> print nopriv_browser.url
339+ http://code.launchpad.dev/product-name.../+recipes
340+
341+ >>> print_recipe_listing_head(nopriv_browser)
342+ Name
343+ Owner
344+ Base Source
345+ Registered
346+
347+ >>> print_recipe_listing_contents(nopriv_browser)
348+ spr-name... Person-name... lp:~.../+git/... ...
349+ spr-name... Person-name... lp:~.../+git/... ...
350+ spr-name... Person-name... lp:~.../+git/... ...
351+ spr-name... Person-name... lp:~.../+git/... ...
352+
353
354 Person Recipe Listings
355 ======================
356@@ -115,7 +200,7 @@
357
358 >>> print_recipe_listing_head(nopriv_browser)
359 Name
360- Base Branch
361+ Base Source
362 Registered
363
364 The listings should now show all recipes whose base branch is a branch from
365
366=== modified file 'lib/lp/code/templates/branch-recipes.pt'
367--- lib/lp/code/templates/branch-recipes.pt 2015-10-05 13:36:06 +0000
368+++ lib/lp/code/templates/branch-recipes.pt 2016-01-12 15:24:34 +0000
369@@ -3,7 +3,7 @@
370 xmlns:metal="http://xml.zope.org/namespaces/metal"
371 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
372 tal:define="context_menu view/context/menu:context"
373- id="related-bugs-and-blueprints">
374+ id="related-recipes">
375
376 <h3>Related source package recipes</h3>
377
378
379=== modified file 'lib/lp/code/templates/gitref-index.pt'
380--- lib/lp/code/templates/gitref-index.pt 2015-09-18 15:41:08 +0000
381+++ lib/lp/code/templates/gitref-index.pt 2016-01-12 15:24:34 +0000
382@@ -7,6 +7,15 @@
383 i18n:domain="launchpad"
384 >
385
386+<metal:block fill-slot="head_epilogue">
387+ <style type="text/css">
388+ #merge-summary, #recipe-summary {
389+ margin-top: .5em;
390+ margin-bottom: .1em;
391+ }
392+ </style>
393+</metal:block>
394+
395 <body>
396
397 <tal:registering metal:fill-slot="registering">
398@@ -24,10 +33,10 @@
399 </div>
400
401 <div class="yui-g">
402- <div id="ref-relations" class="portlet"
403- tal:condition="python: view.show_merge_links or view.show_snap_information">
404+ <div id="ref-relations" class="portlet">
405 <tal:ref-pending-merges
406 replace="structure context/@@++ref-pending-merges" />
407+ <tal:ref-recipes replace="structure context/@@++ref-recipes" />
408 <div metal:use-macro="context/@@+snap-macros/related-snaps" />
409 </div>
410 </div>
411
412=== added file 'lib/lp/code/templates/gitref-recipes.pt'
413--- lib/lp/code/templates/gitref-recipes.pt 1970-01-01 00:00:00 +0000
414+++ lib/lp/code/templates/gitref-recipes.pt 2016-01-12 15:24:34 +0000
415@@ -0,0 +1,19 @@
416+<div
417+ xmlns:tal="http://xml.zope.org/namespaces/tal"
418+ xmlns:metal="http://xml.zope.org/namespaces/metal"
419+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
420+ id="related-recipes">
421+
422+ <h3>Related source package recipes</h3>
423+
424+ <div id="recipe-links" class="actions">
425+ <div id="recipe-summary">
426+ <img src="/@@/source-package-recipe" />
427+ <tal:recipes replace="structure view/recipes_link" />
428+
429+ <a href="/+help-code/related-recipes.html" target="help"
430+ class="sprite maybe action-icon">(?)</a>
431+ </div>
432+ </div>
433+
434+</div>
435
436=== modified file 'lib/lp/code/templates/gitrepository-index.pt'
437--- lib/lp/code/templates/gitrepository-index.pt 2015-12-04 15:48:39 +0000
438+++ lib/lp/code/templates/gitrepository-index.pt 2016-01-12 15:24:34 +0000
439@@ -9,6 +9,10 @@
440
441 <metal:block fill-slot="head_epilogue">
442 <style type="text/css">
443+ #merge-summary, #recipe-summary {
444+ margin-top: .5em;
445+ margin-bottom: .1em;
446+ }
447 #clone-url dt {
448 font-weight: strong;
449 }
450@@ -41,8 +45,9 @@
451 </div>
452
453 <div class="yui-g">
454- <div id="repository-relations" class="portlet"
455- tal:condition="view/show_snap_information">
456+ <div id="repository-relations" class="portlet">
457+ <tal:repository-recipes
458+ replace="structure context/@@++repository-recipes" />
459 <div metal:use-macro="context/@@+snap-macros/related-snaps">
460 <metal:context-type fill-slot="context_type">repository</metal:context-type>
461 </div>
462
463=== added file 'lib/lp/code/templates/gitrepository-recipes.pt'
464--- lib/lp/code/templates/gitrepository-recipes.pt 1970-01-01 00:00:00 +0000
465+++ lib/lp/code/templates/gitrepository-recipes.pt 2016-01-12 15:24:34 +0000
466@@ -0,0 +1,19 @@
467+<div
468+ xmlns:tal="http://xml.zope.org/namespaces/tal"
469+ xmlns:metal="http://xml.zope.org/namespaces/metal"
470+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
471+ id="related-recipes">
472+
473+ <h3>Related source package recipes</h3>
474+
475+ <div id="recipe-links" class="actions">
476+ <div id="recipe-summary">
477+ <img src="/@@/source-package-recipe" />
478+ <tal:recipes replace="structure view/recipes_link" />
479+
480+ <a href="/+help-code/related-recipes.html" target="help"
481+ class="sprite maybe action-icon">(?)</a>
482+ </div>
483+ </div>
484+
485+</div>
486
487=== modified file 'lib/lp/code/templates/sourcepackagerecipe-listing.pt'
488--- lib/lp/code/templates/sourcepackagerecipe-listing.pt 2011-02-23 01:24:09 +0000
489+++ lib/lp/code/templates/sourcepackagerecipe-listing.pt 2016-01-12 15:24:34 +0000
490@@ -15,7 +15,7 @@
491 <tr>
492 <th colspan="2">Name</th>
493 <th tal:condition="view/owner_enabled">Owner</th>
494- <th tal:condition="view/branch_enabled">Base Branch</th>
495+ <th tal:condition="view/branch_enabled">Base Source</th>
496 <th>Registered</th>
497 </tr>
498 </thead>
499@@ -32,8 +32,8 @@
500 </a>
501 </td>
502 <td tal:condition="view/branch_enabled">
503- <a tal:replace="structure recipe/base_branch/fmt:link">
504- Branch
505+ <a tal:replace="structure recipe/base/fmt:link">
506+ Source
507 </a>
508 </td>
509 <td tal:content="recipe/date_created/fmt:datetime" />