← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~wgrant/launchpad/getOwnedProjects into lp:launchpad

 

William Grant has proposed merging lp:~wgrant/launchpad/getOwnedProjects into lp:launchpad.

Commit message:
Extend and export IPerson.getOwnedProjects like getOwnedTeams, so scripts can audit the full ownership hierarchy of important global objects.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~wgrant/launchpad/getOwnedProjects/+merge/210375

Extend and export IPerson.getOwnedProjects like getOwnedTeams, so scripts can audit the full ownership hierarchy of important global objects.
-- 
https://code.launchpad.net/~wgrant/launchpad/getOwnedProjects/+merge/210375
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/getOwnedProjects into lp:launchpad.
=== modified file 'lib/lp/_schema_circular_imports.py'
--- lib/lp/_schema_circular_imports.py	2014-03-07 01:20:34 +0000
+++ lib/lp/_schema_circular_imports.py	2014-03-11 10:35:14 +0000
@@ -355,6 +355,7 @@
 patch_collection_return_type(
     IPerson, 'getArchiveSubscriptions', IArchiveSubscriber)
 patch_entry_return_type(IPerson, 'getRecipe', ISourcePackageRecipe)
+patch_collection_return_type(IPerson, 'getOwnedProjects', IProduct)
 
 # IHasRecipe
 patch_collection_property(

=== modified file 'lib/lp/registry/doc/person.txt'
--- lib/lp/registry/doc/person.txt	2013-08-08 04:11:59 +0000
+++ lib/lp/registry/doc/person.txt	2014-03-11 10:35:14 +0000
@@ -1064,6 +1064,13 @@
     >>> for project in mark.getOwnedProjects():
     ...     print project.displayname
     Derby
+    alsa-utils
+
+We can also ask for projects owned through team memberships.
+
+    >>> for project in mark.getOwnedProjects(transitive=True):
+    ...     print project.displayname
+    Derby
     Tomcat
     alsa-utils
 
@@ -1079,7 +1086,7 @@
     >>> ignored = login_person(registry_member)
     >>> derby = getUtility(IProductSet).getByName('derby')
     >>> derby.active = False
-    >>> for project in mark.getOwnedProjects():
+    >>> for project in mark.getOwnedProjects(transitive=True):
     ...     print project.displayname
     Tomcat
     alsa-utils
@@ -1096,7 +1103,8 @@
 The results returned can be filtered by providing a token to refine the
 search.
 
-    >>> for project in mark.getOwnedProjects(match_name=u'java'):
+    >>> for project in mark.getOwnedProjects(
+    ...     match_name=u'java', transitive=True):
     ...     print project.displayname
     Tomcat
 

=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py	2014-03-11 06:10:53 +0000
+++ lib/lp/registry/interfaces/person.py	2014-03-11 10:35:14 +0000
@@ -1160,7 +1160,11 @@
         maintains, drives, or is the bug supervisor for.
         """
 
-    def getOwnedProjects(match_name=None):
+    @call_with(user=REQUEST_USER)
+    @operation_returns_collection_of(Interface)  # Really IProduct.
+    @export_read_operation()
+    @operation_for_version("devel")
+    def getOwnedProjects(match_name=None, transitive=False, user=None):
         """Projects owned by this person or teams to which she belongs.
 
         :param match_name: string optional project name to screen the results.

=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py	2014-03-11 06:10:53 +0000
+++ lib/lp/registry/model/person.py	2014-03-11 10:35:14 +0000
@@ -1103,17 +1103,29 @@
 
         return DecoratedResultSet(results, get_pillar_name)
 
-    def getOwnedProjects(self, match_name=None):
+    def getOwnedProjects(self, match_name=None, transitive=False, user=None):
         """See `IPerson`."""
         # Import here to work around a circular import problem.
-        from lp.registry.model.product import Product
+        from lp.registry.model.product import (
+            Product,
+            ProductSet,
+            )
 
         clauses = [
             Product.active == True,
-            Product._ownerID == TeamParticipation.teamID,
-            TeamParticipation.person == self,
             ]
 
+        if transitive:
+            # getProductPrivacyFilter may also use TeamParticipation, so
+            # ensure we use a different one.
+            ownership_participation = ClassAlias(TeamParticipation)
+            clauses.extend([
+                Product._ownerID == ownership_participation.teamID,
+                ownership_participation.person == self,
+                ])
+        else:
+            clauses.append(Product._ownerID == self.id)
+
         # We only want to use the extra query if match_name is not None and it
         # is not the empty string ('' or u'').
         if match_name:
@@ -1122,6 +1134,10 @@
                     Product.name.contains_string(match_name),
                     Product.displayname.contains_string(match_name),
                     fti_search(Product, match_name)))
+
+        if user is not None:
+            clauses.append(ProductSet.getProductPrivacyFilter(user))
+
         return IStore(Product).find(
             Product, *clauses
             ).config(distinct=True).order_by(Product.displayname)

=== modified file 'lib/lp/registry/vocabularies.py'
--- lib/lp/registry/vocabularies.py	2014-01-07 14:02:51 +0000
+++ lib/lp/registry/vocabularies.py	2014-03-11 10:35:14 +0000
@@ -1438,7 +1438,7 @@
         if self.is_commercial_admin:
             projects = self.product_set.search(user, query)
         else:
-            projects = user.getOwnedProjects(match_name=query)
+            projects = user.getOwnedProjects(match_name=query, transitive=True)
         return projects
 
     def toTerm(self, project):