launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #26720
[Merge] ~pappacena/launchpad:ocirecipe-list-on-person into launchpad:master
Thiago F. Pappacena has proposed merging ~pappacena/launchpad:ocirecipe-list-on-person into launchpad:master with ~pappacena/launchpad:ocirecipe-filter-private as a prerequisite.
Commit message:
Adding ~user/+oci-recipes page, with the list of all OCI recipes owned by a user
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/400214
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad:ocirecipe-list-on-person into launchpad:master.
diff --git a/lib/lp/oci/browser/configure.zcml b/lib/lp/oci/browser/configure.zcml
index 53db0d2..bb68145 100644
--- a/lib/lp/oci/browser/configure.zcml
+++ b/lib/lp/oci/browser/configure.zcml
@@ -36,6 +36,12 @@
name="+recipes"
template="../templates/ociproject-recipes.pt" />
<browser:page
+ for="lp.registry.interfaces.person.IPerson"
+ class="lp.oci.browser.ocirecipe.OCIProjectRecipesView"
+ permission="launchpad.View"
+ name="+oci-recipes"
+ template="../templates/ociproject-recipes.pt" />
+ <browser:page
for="lp.registry.interfaces.ociproject.IOCIProject"
class="lp.oci.browser.ocirecipe.OCIRecipeAddView"
permission="launchpad.AnyLegitimatePerson"
diff --git a/lib/lp/oci/browser/ocirecipe.py b/lib/lp/oci/browser/ocirecipe.py
index 5eee7e3..4d526ff 100644
--- a/lib/lp/oci/browser/ocirecipe.py
+++ b/lib/lp/oci/browser/ocirecipe.py
@@ -204,7 +204,9 @@ class OCIRecipeContextMenu(ContextMenu):
class OCIProjectRecipesView(LaunchpadView):
- """Default view for the list of OCI recipes of an OCI project."""
+ """Default view for the list of OCI recipes of a context (OCI project
+ or Person).
+ """
page_title = 'Recipes'
@property
@@ -217,7 +219,7 @@ class OCIProjectRecipesView(LaunchpadView):
@property
def recipes(self):
- recipes = getUtility(IOCIRecipeSet).findByOCIProject(
+ recipes = getUtility(IOCIRecipeSet).findByContext(
self.context, visible_by_user=self.user)
return recipes.order_by('name')
diff --git a/lib/lp/oci/browser/tests/test_ocirecipe.py b/lib/lp/oci/browser/tests/test_ocirecipe.py
index 57d7c49..abd3180 100644
--- a/lib/lp/oci/browser/tests/test_ocirecipe.py
+++ b/lib/lp/oci/browser/tests/test_ocirecipe.py
@@ -2019,6 +2019,30 @@ class TestOCIProjectRecipesView(BaseTestOCIRecipeView):
**kwargs)
for _ in range(count)]
+ def test_oci_recipe_list_for_person(self):
+ owner = self.factory.makePerson(name="recipe-owner")
+ for i in range(2):
+ self.factory.makeOCIRecipe(
+ name="my-oci-recipe-%s" % i, owner=owner, registrant=owner)
+
+ # This recipe should not be present.
+ someone_else = self.factory.makePerson()
+ self.factory.makeOCIRecipe(owner=someone_else, registrant=someone_else)
+
+ # self.person now visits ~owner/+oci-recipes page.
+ browser = self.getViewBrowser(owner, "+oci-recipes", user=self.person)
+ main_text = extract_text(find_main_content(browser.contents))
+ self.assertTextMatchesExpressionIgnoreWhitespace(
+ """
+ OCI recipes for recipe-owner
+ There are 2 recipes registered for recipe-owner.
+ Name Owner Source Build file Date created
+ my-oci-recipe-0 Recipe-owner .*
+ my-oci-recipe-1 Recipe-owner .*
+ 1 .* 2 of 2 results
+ First .* Previous .* Next .* Last
+ """, main_text)
+
def test_shows_no_recipe(self):
"""Should shows correct message when there are no visible recipes."""
# Create a private OCI recipe that should not be shown.
diff --git a/lib/lp/oci/interfaces/ocirecipe.py b/lib/lp/oci/interfaces/ocirecipe.py
index 5ed23a7..2be485e 100644
--- a/lib/lp/oci/interfaces/ocirecipe.py
+++ b/lib/lp/oci/interfaces/ocirecipe.py
@@ -555,6 +555,9 @@ class IOCIRecipeSet(Interface):
def findByOCIProject(oci_project, visible_by_user=None):
"""Return all OCI recipes with the given `oci_project`."""
+ def findByContext(context, visible_by_user=None):
+ """Returns all OCI recipes for a given context."""
+
def preloadDataForOCIRecipes(recipes, user):
"""Load the data related to a list of OCI Recipes."""
diff --git a/lib/lp/oci/model/ocirecipe.py b/lib/lp/oci/model/ocirecipe.py
index 2a5598a..1397837 100644
--- a/lib/lp/oci/model/ocirecipe.py
+++ b/lib/lp/oci/model/ocirecipe.py
@@ -105,7 +105,9 @@ from lp.registry.interfaces.accesspolicy import (
IAccessArtifactSource,
)
from lp.registry.interfaces.distribution import IDistributionSet
+from lp.registry.interfaces.ociproject import IOCIProject
from lp.registry.interfaces.person import (
+ IPerson,
IPersonSet,
validate_public_person,
)
@@ -875,6 +877,14 @@ class OCIRecipeSet:
OCIRecipe.oci_project == oci_project,
get_ocirecipe_privacy_filter(visible_by_user))
+ def findByContext(self, context, visible_by_user):
+ if IPerson.providedBy(context):
+ return self.findByOwner(context).find(
+ get_ocirecipe_privacy_filter(visible_by_user))
+ if IOCIProject.providedBy(context):
+ return self.findByOCIProject(context, visible_by_user)
+ raise NotImplementedError("Unknown OCI recipe context: %s" % context)
+
def findByGitRepository(self, repository, paths=None):
"""See `IOCIRecipeSet`."""
clauses = [OCIRecipe.git_repository == repository]
diff --git a/lib/lp/registry/browser/person.py b/lib/lp/registry/browser/person.py
index 002b94a..6d5d8da 100644
--- a/lib/lp/registry/browser/person.py
+++ b/lib/lp/registry/browser/person.py
@@ -139,7 +139,10 @@ from lp.code.errors import InvalidNamespace
from lp.code.interfaces.branchnamespace import IBranchNamespaceSet
from lp.code.interfaces.gitlookup import IGitTraverser
from lp.oci.interfaces.ocipushrule import IOCIPushRuleSet
-from lp.oci.interfaces.ocirecipe import IOCIRecipe
+from lp.oci.interfaces.ocirecipe import (
+ IOCIRecipe,
+ IOCIRecipeSet,
+ )
from lp.oci.interfaces.ociregistrycredentials import (
IOCIRegistryCredentialsSet,
OCIRegistryCredentialsAlreadyExist,
@@ -774,6 +777,13 @@ class CommonMenuLinks:
enabled = user_can_edit_credentials_for_owner(self.context, self.user)
return Link(target, text, enabled=enabled, icon='info')
+ def oci_recipes(self):
+ target = '+oci-recipes'
+ text = 'OCI recipes'
+ enabled = not getUtility(IOCIRecipeSet).findByContext(
+ self.context, visible_by_user=self.user).is_empty()
+ return Link(target, text, enabled=enabled, icon='info')
+
class PersonMenuMixin(CommonMenuLinks):
@@ -838,6 +848,7 @@ class PersonOverviewMenu(ApplicationMenu, PersonMenuMixin,
'ppa',
'oauth_tokens',
'oci_registry_credentials',
+ 'oci_recipes',
'related_software_summary',
'view_recipes',
'view_snaps',
diff --git a/lib/lp/registry/browser/tests/test_person.py b/lib/lp/registry/browser/tests/test_person.py
index 1bd1f8f..bbc2f4e 100644
--- a/lib/lp/registry/browser/tests/test_person.py
+++ b/lib/lp/registry/browser/tests/test_person.py
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-# Copyright 2009-2020 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2021 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
from __future__ import unicode_literals
@@ -465,6 +465,49 @@ class TestPersonIndexView(BrowserTestCase):
self.assertNotEqual('', markup)
self.assertThat(markup, Not(link_match))
+ def test_show_oci_recipes_link(self):
+ self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
+ person = self.factory.makePerson()
+ # Creates a recipe, so the link appears.
+ self.factory.makeOCIRecipe(owner=person, registrant=person)
+ view = create_initialized_view(person, '+index', principal=person)
+ with person_logged_in(person):
+ markup = self.get_markup(view, person)
+ expected_url = 'http://launchpad.test/~%s/+oci-recipes' % person.name
+ link_match = soupmatchers.HTMLContains(
+ soupmatchers.Tag(
+ 'OCI recipes link', 'a',
+ attrs={
+ 'href': expected_url},
+ text='OCI recipes'))
+ self.assertThat(markup, link_match)
+
+ login(ANONYMOUS)
+ markup = self.get_markup(view, person)
+ self.assertThat(markup, link_match)
+
+ def test_hides_oci_recipes_link_if_user_doesnt_have_oci_recipes(self):
+ self.useFixture(FeatureFixture({OCI_RECIPE_ALLOW_CREATE: "on"}))
+ person = self.factory.makePerson()
+ # Creates a recipe from another user, just to make sure it will not
+ # interfere.
+ self.factory.makeOCIRecipe()
+ view = create_initialized_view(person, '+index', principal=person)
+ with person_logged_in(person):
+ markup = self.get_markup(view, person)
+ expected_url = 'http://launchpad.test/~%s/+oci-recipes' % person.name
+ link_match = soupmatchers.HTMLContains(
+ soupmatchers.Tag(
+ 'OCI recipes link', 'a',
+ attrs={
+ 'href': expected_url},
+ text='OCI recipes'))
+ self.assertThat(markup, Not(link_match))
+
+ login(ANONYMOUS)
+ markup = self.get_markup(view, person)
+ self.assertThat(markup, Not(link_match))
+
def test_ppas_query_count(self):
owner = self.factory.makePerson()
diff --git a/lib/lp/registry/templates/person-index.pt b/lib/lp/registry/templates/person-index.pt
index 1261219..708c477 100644
--- a/lib/lp/registry/templates/person-index.pt
+++ b/lib/lp/registry/templates/person-index.pt
@@ -92,6 +92,10 @@
tal:define="link context/menu:overview/oci_registry_credentials"
tal:condition="link/enabled"
tal:content="structure link/fmt:link" />
+ <li
+ tal:define="link context/menu:overview/oci_recipes"
+ tal:condition="link/enabled"
+ tal:content="structure link/fmt:link" />
</ul>
<div class="yui-g">
Follow ups