← Back to team overview

launchpad-reviewers team mailing list archive

[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