launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #12577
[Merge] lp:~abentley/launchpad/hide-sprint-blueprints into lp:launchpad
Aaron Bentley has proposed merging lp:~abentley/launchpad/hide-sprint-blueprints into lp:launchpad with lp:~abentley/launchpad/specification-cleanup as a prerequisite.
Commit message:
Respect privacy listing specifications for sprints.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1051029 in Launchpad itself: "PROPRIETARY specifications break meeting listings"
https://bugs.launchpad.net/launchpad/+bug/1051029
For more details, see:
https://code.launchpad.net/~abentley/launchpad/hide-sprint-blueprints/+merge/126792
= Summary =
Fix bug #1051029: Fix meeting listings with PROPRIETARY specs
== Proposed fix ==
Sprint.specification filters un-viewable specs out of listings
== Pre-implementation notes ==
None
== LOC Rationale ==
Part of private projects
== Implementation details ==
IHasSpecifications.specifications() and all implementations accept a user as a mandatory parameter (but that user may be None if no Person is logged in).
Sprint.specifications uses the user to invoke get_specification_privacy_filter()
All callers are updated to supply a user. View methods supply self.user, and model methods accept an additional parameter. _all_specifications and _valid_specifications, as model properties, cannot accept parameters, so they use ILaunchBag.user.
== Tests ==
Everything
== Demo and Q/A ==
Create a PROPRIETARY blueprint. Link it to a sprint. Go to the sprint page. You should see the blueprint. Log out. It should stop being listed, but the page should not be broken.
= Launchpad lint =
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/blueprints/templates/specifications-portlet-latestregistered.pt
lib/lp/blueprints/tests/test_hasspecifications.py
lib/lp/services/database/stormexpr.py
lib/lp/registry/doc/milestone.txt
lib/lp/registry/model/projectgroup.py
lib/lp/registry/doc/distribution.txt
lib/lp/registry/model/productseries.py
lib/lp/blueprints/interfaces/specificationtarget.py
lib/lp/app/browser/root.py
lib/lp/blueprints/templates/specificationtarget-assignments.pt
lib/lp/blueprints/browser/specificationtarget.py
lib/lp/blueprints/model/tests/test_sprint.py
lib/lp/bugs/model/tests/test_bugtask.py
lib/lp/blueprints/templates/person-specworkload.pt
lib/lp/blueprints/browser/sprint.py
lib/lp/blueprints/templates/specifications-portlet-stats.pt
lib/lp/testing/factory.py
lib/lp/registry/doc/projectgroup.txt
lib/lp/blueprints/model/specification.py
lib/lp/registry/browser/__init__.py
lib/lp/blueprints/model/sprint.py
lib/lp/blueprints/templates/specifications-portlet-latestcompleted.pt
lib/lp/testing/_webservice.py
lib/lp/registry/model/product.py
lib/lp/blueprints/browser/configure.zcml
lib/lp/blueprints/browser/tests/test_sprint.py
lib/lp/blueprints/doc/specification.txt
lib/lp/blueprints/browser/specification.py
lib/lp/registry/browser/person.py
lib/lp/registry/model/person.py
lib/lp/registry/model/distroseries.py
lib/lp/_schema_circular_imports.py
lib/lp/registry/model/distribution.py
lib/lp/blueprints/interfaces/specification.py
./lib/lp/registry/model/product.py
408: redefinition of function 'date_next_suggest_packaging' from line 400
^^^ This is an acceptable case of re-definition. It's creating a setter.
--
https://code.launchpad.net/~abentley/launchpad/hide-sprint-blueprints/+merge/126792
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~abentley/launchpad/hide-sprint-blueprints into lp:launchpad.
=== modified file 'lib/lp/app/browser/root.py'
--- lib/lp/app/browser/root.py 2012-09-27 20:24:22 +0000
+++ lib/lp/app/browser/root.py 2012-09-27 20:24:22 +0000
@@ -130,7 +130,7 @@
@property
def blueprint_count(self):
"""The total blueprint count in all of Launchpad."""
- return getUtility(ISpecificationSet).specificationCount()
+ return getUtility(ISpecificationSet).specificationCount(self.user)
@property
def answer_count(self):
=== modified file 'lib/lp/blueprints/browser/specification.py'
--- lib/lp/blueprints/browser/specification.py 2012-09-27 20:24:22 +0000
+++ lib/lp/blueprints/browser/specification.py 2012-09-27 20:24:22 +0000
@@ -1532,17 +1532,17 @@
@property
def latest_specifications(self):
return self.context.specifications(
- sort=SpecificationSort.DATE, quantity=5)
+ self.user, sort=SpecificationSort.DATE, quantity=5)
@property
def latest_completed_specifications(self):
return self.context.specifications(
- sort=SpecificationSort.DATE, quantity=5,
+ self.user, sort=SpecificationSort.DATE, quantity=5,
filter=[SpecificationFilter.COMPLETE])
@property
def specification_count(self):
- return self.context.specificationCount()
+ return self.context.specificationCount(self.user)
@safe_action
@action('Find blueprints', name="search")
=== modified file 'lib/lp/blueprints/browser/specificationtarget.py'
--- lib/lp/blueprints/browser/specificationtarget.py 2012-09-27 20:24:22 +0000
+++ lib/lp/blueprints/browser/specificationtarget.py 2012-09-27 20:24:22 +0000
@@ -259,11 +259,11 @@
@cachedproperty
def has_any_specifications(self):
- return self.context._all_specifications.count() != 0
+ return not self.context._all_specifications.is_empty()
@cachedproperty
def all_specifications(self):
- return shortlist(self.context.all_specifications)
+ return shortlist(self.context.all_specifications(self.user))
@cachedproperty
def searchrequested(self):
@@ -348,7 +348,7 @@
and not check_permission('launchpad.View', self.context)):
return []
filter = self.spec_filter
- return self.context.specifications(filter=filter)
+ return self.context.specifications(self.user, filter=filter)
@cachedproperty
def specs_batched(self):
@@ -364,7 +364,7 @@
def documentation(self):
filter = [SpecificationFilter.COMPLETE,
SpecificationFilter.INFORMATIONAL]
- return shortlist(self.context.specifications(filter=filter))
+ return shortlist(self.context.specifications(self.user, filter=filter))
@cachedproperty
def categories(self):
@@ -405,8 +405,9 @@
Only ACCEPTED specifications are returned. This list is used by the
+portlet-latestspecs view.
"""
- return self.context.specifications(sort=SpecificationSort.DATE,
- quantity=quantity, prejoin_people=False)
+ return self.context.specifications(self.user,
+ sort=SpecificationSort.DATE, quantity=quantity,
+ prejoin_people=False)
class SpecificationAssignmentsView(HasSpecificationsView):
=== modified file 'lib/lp/blueprints/browser/sprint.py'
--- lib/lp/blueprints/browser/sprint.py 2012-01-01 02:58:52 +0000
+++ lib/lp/blueprints/browser/sprint.py 2012-09-27 20:24:22 +0000
@@ -216,7 +216,7 @@
@cachedproperty
def latest_approved(self):
filter = [SpecificationFilter.ACCEPTED]
- return self.context.specifications(filter=filter,
+ return self.context.specifications(self.user, filter=filter,
quantity=self.latest_specs_limit,
sort=SpecificationSort.DATE)
@@ -464,7 +464,7 @@
model_specs = []
for spec in self.context.specifications(
- filter=[SpecificationFilter.ACCEPTED]):
+ self.user, filter=[SpecificationFilter.ACCEPTED]):
# skip sprints with no priority or less than low:
if spec.priority < SpecificationPriority.UNDEFINED:
=== modified file 'lib/lp/blueprints/browser/tests/test_sprint.py'
--- lib/lp/blueprints/browser/tests/test_sprint.py 2012-09-27 20:24:22 +0000
+++ lib/lp/blueprints/browser/tests/test_sprint.py 2012-09-27 20:24:22 +0000
@@ -8,6 +8,7 @@
from storm.locals import Store
from testtools.matchers import Equals
+from lp.app.enums import InformationType
from lp.testing import BrowserTestCase
from lp.testing.layers import DatabaseFunctionalLayer
from lp.testing.matchers import BrowsesWithQueryLimit, HasQueryCount
@@ -40,3 +41,15 @@
with QueryCollector() as recorder:
self.getViewBrowser(sprint)
self.assertThat(recorder, HasQueryCount(Equals(30)))
+
+ def test_proprietary_blueprint_listing_query_count(self):
+ """Set a maximum number of queries for sprint blueprint lists."""
+ sprint = self.factory.makeSprint()
+ for count in range(10):
+ blueprint = self.factory.makeSpecification(
+ information_type=InformationType.PROPRIETARY)
+ link = blueprint.linkSprint(sprint, blueprint.owner)
+ link.acceptBy(sprint.owner)
+ with QueryCollector() as recorder:
+ self.getViewBrowser(sprint)
+ self.assertThat(recorder, HasQueryCount(Equals(22)))
=== modified file 'lib/lp/blueprints/doc/specification.txt'
--- lib/lp/blueprints/doc/specification.txt 2012-09-27 20:24:22 +0000
+++ lib/lp/blueprints/doc/specification.txt 2012-09-27 20:24:22 +0000
@@ -222,7 +222,7 @@
We can filter for specifications that contain specific text, across all
specifications:
- >>> for spec in specset.specifications(filter=['install']):
+ >>> for spec in specset.specifications(None, filter=['install']):
... print spec.name, spec.target.name
cluster-installation kubuntu
extension-manager-upgrades firefox
@@ -239,7 +239,7 @@
>>> unlink_source_packages(upstream_firefox)
>>> upstream_firefox.active = False
>>> flush_database_updates()
- >>> for spec in specset.specifications(filter=['install']):
+ >>> for spec in specset.specifications(None, filter=['install']):
... print spec.name, spec.target.name
cluster-installation kubuntu
media-integrity-check ubuntu
=== modified file 'lib/lp/blueprints/interfaces/specification.py'
--- lib/lp/blueprints/interfaces/specification.py 2012-09-27 20:24:22 +0000
+++ lib/lp/blueprints/interfaces/specification.py 2012-09-27 20:24:22 +0000
@@ -674,7 +674,7 @@
coming_sprints = Attribute("The next 5 sprints in the system.")
- def specificationCount():
+ def specificationCount(user):
"""The total number of blueprints in Launchpad"""
def getStatusCountsForProductSeries(product_series):
=== modified file 'lib/lp/blueprints/interfaces/specificationtarget.py'
--- lib/lp/blueprints/interfaces/specificationtarget.py 2012-09-27 20:24:22 +0000
+++ lib/lp/blueprints/interfaces/specificationtarget.py 2012-09-27 20:24:22 +0000
@@ -60,10 +60,11 @@
'have not been accepted for that goal'))),
exported_as="valid_specifications", as_of="devel")
- def specifications(quantity=None, sort=None, filter=None,
+ def specifications(user, quantity=None, sort=None, filter=None,
prejoin_people=True):
"""Specifications for this target.
+ The user specifies which user to use for calculation of visibility.
The sort is a dbschema which indicates the preferred sort order. The
filter is an indicator of the kinds of specs to be returned, and
appropriate filters depend on the kind of object this method is on.
=== modified file 'lib/lp/blueprints/model/specification.py'
--- lib/lp/blueprints/model/specification.py 2012-09-27 20:24:22 +0000
+++ lib/lp/blueprints/model/specification.py 2012-09-27 20:24:22 +0000
@@ -109,6 +109,7 @@
cachedproperty,
get_property_cache,
)
+from lp.services.webapp.interfaces import ILaunchBag
def recursive_blocked_query(spec):
@@ -957,7 +958,7 @@
for other classes that have specifications.
"""
- def specifications(self, sort=None, quantity=None, filter=None,
+ def specifications(self, user, sort=None, quantity=None, filter=None,
prejoin_people=True):
"""See IHasSpecifications."""
# this should be implemented by the actual context class
@@ -1024,16 +1025,19 @@
@property
def _all_specifications(self):
"""See IHasSpecifications."""
- return self.specifications(filter=[SpecificationFilter.ALL])
+ user = getUtility(ILaunchBag).user
+ return self.specifications(user, filter=[SpecificationFilter.ALL])
@property
def _valid_specifications(self):
"""See IHasSpecifications."""
- return self.specifications(filter=[SpecificationFilter.VALID])
+ user = getUtility(ILaunchBag).user
+ return self.specifications(user, filter=[SpecificationFilter.VALID])
- def specificationCount(self):
+ def specificationCount(self, user):
"""See IHasSpecifications."""
- return self.specifications(filter=[SpecificationFilter.ALL]).count()
+ return self.specifications(user,
+ filter=[SpecificationFilter.ALL]).count()
class SpecificationSet(HasSpecificationsMixin):
@@ -1072,7 +1076,7 @@
"""See ISpecificationSet."""
return iter(self.all_specifications)
- def specifications(self, sort=None, quantity=None, filter=None,
+ def specifications(self, user, sort=None, quantity=None, filter=None,
prejoin_people=True):
"""See IHasSpecifications."""
=== modified file 'lib/lp/blueprints/model/sprint.py'
--- lib/lp/blueprints/model/sprint.py 2012-09-27 20:24:22 +0000
+++ lib/lp/blueprints/model/sprint.py 2012-09-27 20:24:22 +0000
@@ -41,7 +41,10 @@
ISprint,
ISprintSet,
)
-from lp.blueprints.model.specification import HasSpecificationsMixin
+from lp.blueprints.model.specification import (
+ get_specification_privacy_filter,
+ HasSpecificationsMixin,
+ )
from lp.blueprints.model.sprintattendance import SprintAttendance
from lp.blueprints.model.sprintspecification import SprintSpecification
from lp.registry.interfaces.person import (
@@ -111,7 +114,7 @@
# Only really used in tests.
return [a.attendee for a in self.attendances]
- def spec_filter_clause(self, filter=None):
+ def spec_filter_clause(self, user, filter=None):
"""Figure out the appropriate query for specifications on a sprint.
We separate out the query generation from the normal
@@ -126,6 +129,7 @@
Or(Specification.product == None,
Not(Specification.productID.is_in(Select(Product.id,
Product.active == False))))]
+ query.append(get_specification_privacy_filter(user))
if not filter:
# filter could be None or [] then we decide the default
# which for a sprint is to show everything approved
@@ -169,7 +173,10 @@
query.append(fti_search(Specification, constraint))
return query
- def specifications(self, sort=None, quantity=None, filter=None,
+ def all_specifications(self, user):
+ return self.specifications(user, filter=[SpecificationFilter.ALL])
+
+ def specifications(self, user, sort=None, quantity=None, filter=None,
prejoin_people=False):
"""See IHasSpecifications."""
# prejoin_people is provided only for interface compatibility and
@@ -177,7 +184,7 @@
assert not prejoin_people
if filter is None:
filter = set([SpecificationFilter.ACCEPTED])
- query = self.spec_filter_clause(filter=filter)
+ query = self.spec_filter_clause(user, filter=filter)
# import here to avoid circular deps
from lp.blueprints.model.specification import Specification
results = Store.of(self).find(Specification, *query)
@@ -200,7 +207,7 @@
def specificationLinks(self, filter=None):
"""See `ISprint`."""
- query = self.spec_filter_clause(filter=filter)
+ query = self.spec_filter_clause(None, filter=filter)
result = Store.of(self).find(SprintSpecification, *query)
return result
@@ -227,7 +234,7 @@
# queue
flush_database_updates()
- return self.specifications(
+ return self.specifications(decider,
filter=[SpecificationFilter.PROPOSED]).count()
def declineSpecificationLinks(self, idlist, decider):
@@ -241,7 +248,7 @@
# queue
flush_database_updates()
- return self.specifications(
+ return self.specifications(decider,
filter=[SpecificationFilter.PROPOSED]).count()
# attendance
=== modified file 'lib/lp/blueprints/model/tests/test_sprint.py'
--- lib/lp/blueprints/model/tests/test_sprint.py 2012-09-27 20:24:22 +0000
+++ lib/lp/blueprints/model/tests/test_sprint.py 2012-09-27 20:24:22 +0000
@@ -9,8 +9,10 @@
import datetime
from pytz import utc
+from zope.component import getUtility
from zope.security.proxy import removeSecurityProxy
+from lp.app.enums import InformationType
from lp.blueprints.enums import (
NewSpecificationDefinitionStatus,
SpecificationDefinitionStatus,
@@ -18,12 +20,13 @@
SpecificationPriority,
SpecificationSort,
)
+from lp.registry.interfaces.accesspolicy import IAccessPolicySource
from lp.testing import TestCaseWithFactory
from lp.testing.layers import DatabaseFunctionalLayer
-def list_result(sprint, filter=None):
- result = sprint.specifications(SpecificationSort.DATE, filter=filter)
+def list_result(sprint, filter=None, user=None):
+ result = sprint.specifications(user, SpecificationSort.DATE, filter=filter)
return list(result)
@@ -38,11 +41,12 @@
def makeSpec(self, sprint=None, date_decided=0, date_created=0,
proposed=False, declined=False, title=None,
status=NewSpecificationDefinitionStatus.NEW,
- name=None, priority=None):
+ name=None, priority=None, information_type=None):
if sprint is None:
sprint = self.factory.makeSprint()
blueprint = self.factory.makeSpecification(
- title=title, status=status, name=name, priority=priority)
+ title=title, status=status, name=name, priority=priority,
+ information_type=information_type)
link = blueprint.linkSprint(sprint, blueprint.owner)
naked_link = removeSecurityProxy(link)
if declined:
@@ -61,10 +65,11 @@
sprint = self.factory.makeSprint()
for count in range(10):
self.makeSpec(sprint)
- self.assertEqual(10, sprint.specifications().count())
- self.assertEqual(10, sprint.specifications(quantity=None).count())
- self.assertEqual(8, sprint.specifications(quantity=8).count())
- self.assertEqual(10, sprint.specifications(quantity=11).count())
+ self.assertEqual(10, sprint.specifications(None).count())
+ result = sprint.specifications(None, quantity=None).count()
+ self.assertEqual(10, result)
+ self.assertEqual(8, sprint.specifications(None, quantity=8).count())
+ self.assertEqual(10, sprint.specifications(None, quantity=11).count())
def test_specifications_date_sort_accepted_decided(self):
# If only accepted proposals are requested, date-sorting uses
@@ -133,9 +138,9 @@
blueprint3 = self.makeSpec(
sprint, priority=SpecificationPriority.LOW,
status=SpecificationDefinitionStatus.OBSOLETE)
- result = sprint.specifications()
+ result = sprint.specifications(None)
self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
- result = sprint.specifications(sort=SpecificationSort.PRIORITY)
+ result = sprint.specifications(None, sort=SpecificationSort.PRIORITY)
self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
def test_priority_sort_fallback_status(self):
@@ -148,9 +153,9 @@
sprint, status=SpecificationDefinitionStatus.APPROVED, name='c')
blueprint3 = self.makeSpec(
sprint, status=SpecificationDefinitionStatus.NEW, name='b')
- result = sprint.specifications()
+ result = sprint.specifications(None)
self.assertEqual([blueprint2, blueprint3, blueprint1], list(result))
- result = sprint.specifications(sort=SpecificationSort.PRIORITY)
+ result = sprint.specifications(None, sort=SpecificationSort.PRIORITY)
self.assertEqual([blueprint2, blueprint3, blueprint1], list(result))
def test_priority_sort_fallback_name(self):
@@ -159,9 +164,9 @@
sprint = blueprint1.sprints[0]
blueprint2 = self.makeSpec(sprint, name='c')
blueprint3 = self.makeSpec(sprint, name='a')
- result = sprint.specifications()
+ result = sprint.specifications(None)
self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
- result = sprint.specifications(sort=SpecificationSort.PRIORITY)
+ result = sprint.specifications(None, sort=SpecificationSort.PRIORITY)
self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
def test_text_search(self):
@@ -182,6 +187,34 @@
result = list_result(sprint, [SpecificationFilter.DECLINED])
self.assertEqual([blueprint2], result)
+ def test_proprietary_not_listed(self):
+ # Proprietary blueprints are not listed for random users
+ blueprint1 = self.makeSpec(
+ information_type=InformationType.PROPRIETARY)
+ self.assertEqual([], list_result(blueprint1.sprints[0]))
+
+ def test_proprietary_listed_for_artifact_grant(self):
+ # Proprietary blueprints are listed for users with an artifact grant.
+ blueprint1 = self.makeSpec(
+ information_type=InformationType.PROPRIETARY)
+ grant = self.factory.makeAccessArtifactGrant(
+ concrete_artifact=blueprint1)
+ self.assertEqual(
+ [blueprint1],
+ list_result(blueprint1.sprints[0], user=grant.grantee))
+
+ def test_proprietary_listed_for_policy_grant(self):
+ # Proprietary blueprints are listed for users with a policy grant.
+ blueprint1 = self.makeSpec(
+ information_type=InformationType.PROPRIETARY)
+ policy_source = getUtility(IAccessPolicySource)
+ (policy,) = policy_source.find(
+ [(blueprint1.product, InformationType.PROPRIETARY)])
+ grant = self.factory.makeAccessPolicyGrant(policy)
+ self.assertEqual(
+ [blueprint1],
+ list_result(blueprint1.sprints[0], user=grant.grantee))
+
class TestSprintAttendancesSort(TestCaseWithFactory):
=== modified file 'lib/lp/blueprints/templates/person-specworkload.pt'
--- lib/lp/blueprints/templates/person-specworkload.pt 2011-12-08 22:41:00 +0000
+++ lib/lp/blueprints/templates/person-specworkload.pt 2012-09-27 20:24:22 +0000
@@ -44,7 +44,7 @@
<tal:participants repeat="member members_batch">
- <tal:specs define="specifications member/specifications">
+ <tal:specs define="specifications member/@@+specworkload/specifications">
<div style="margin-bottom: 1em;" tal:condition="specifications">
<p>
=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py 2012-09-11 13:25:48 +0000
+++ lib/lp/registry/browser/person.py 2012-09-27 20:24:22 +0000
@@ -1303,6 +1303,9 @@
batch_nav = BatchNavigator(members, self.request, size=20)
return batch_nav
+ def specifications(self):
+ return self.context.specifications(self.user)
+
class PersonSpecWorkloadTableView(LaunchpadView):
"""View to render the specification workload table for a person.
@@ -1331,7 +1334,7 @@
approver, the assignee or the drafter.
"""
return [PersonSpecWorkloadTableView.PersonSpec(spec, self.context)
- for spec in self.context.specifications()]
+ for spec in self.context.specifications(self.user)]
class PersonVouchersView(LaunchpadFormView):
=== modified file 'lib/lp/registry/doc/distribution.txt'
--- lib/lp/registry/doc/distribution.txt 2012-09-27 20:24:22 +0000
+++ lib/lp/registry/doc/distribution.txt 2012-09-27 20:24:22 +0000
@@ -490,18 +490,18 @@
complete so it will not show up unless we explicitly ask for complete specs:
>>> filter = [SpecificationFilter.INFORMATIONAL]
- >>> kubuntu.specifications(filter=filter).count()
+ >>> kubuntu.specifications(None, filter=filter).count()
0
>>> filter = [SpecificationFilter.INFORMATIONAL,
... SpecificationFilter.COMPLETE]
- >>> kubuntu.specifications(filter=filter).count()
+ >>> kubuntu.specifications(None, filter=filter).count()
1
There are 2 completed specs for Kubuntu:
>>> filter = [SpecificationFilter.COMPLETE]
- >>> for spec in kubuntu.specifications(filter=filter):
+ >>> for spec in kubuntu.specifications(None, filter=filter):
... print spec.name, spec.is_complete
thinclient-local-devices True
usplash-on-hibernation True
@@ -510,7 +510,7 @@
And there are four incomplete specs:
>>> filter = [SpecificationFilter.INCOMPLETE]
- >>> for spec in kubuntu.specifications(filter=filter):
+ >>> for spec in kubuntu.specifications(None, filter=filter):
... print spec.name, spec.is_complete
cluster-installation False
revu False
@@ -521,7 +521,7 @@
If we ask for all specs, we get them in the order of priority.
>>> filter = [SpecificationFilter.ALL]
- >>> for spec in kubuntu.specifications(filter=filter):
+ >>> for spec in kubuntu.specifications(None, filter=filter):
... print spec.priority.title, spec.name
Essential cluster-installation
High revu
@@ -533,7 +533,7 @@
And if we ask just for specs, we get the incomplete ones.
- >>> for spec in kubuntu.specifications():
+ >>> for spec in kubuntu.specifications(None):
... print spec.name, spec.is_complete
cluster-installation False
revu False
@@ -542,7 +542,7 @@
We can filter for specifications that contain specific text:
- >>> for spec in kubuntu.specifications(filter=['package']):
+ >>> for spec in kubuntu.specifications(None, filter=['package']):
... print spec.name
revu
@@ -550,7 +550,7 @@
>>> from lp.blueprints.enums import SpecificationDefinitionStatus
>>> login('mark@xxxxxxxxxxx')
- >>> for spec in kubuntu.specifications():
+ >>> for spec in kubuntu.specifications(None):
... # Do this here, otherwise, the change will be flush before
... # updateLifecycleStatus() acts and an IntegrityError will be
... # raised.
=== modified file 'lib/lp/registry/doc/projectgroup.txt'
--- lib/lp/registry/doc/projectgroup.txt 2012-09-27 20:24:22 +0000
+++ lib/lp/registry/doc/projectgroup.txt 2012-09-27 20:24:22 +0000
@@ -150,7 +150,7 @@
First, there should be only one informational spec for mozilla:
>>> filter = [SpecificationFilter.INFORMATIONAL]
- >>> for spec in mozilla.specifications(filter=filter):
+ >>> for spec in mozilla.specifications(None, filter=filter):
... print spec.name
extension-manager-upgrades
@@ -158,19 +158,19 @@
There are no completed specs for mozilla:
>>> filter = [SpecificationFilter.COMPLETE]
- >>> for spec in mozilla.specifications(filter=filter):
+ >>> for spec in mozilla.specifications(None, filter=filter):
... print spec.name
And there are five incomplete specs:
>>> filter = [SpecificationFilter.INCOMPLETE]
- >>> mozilla.specifications(filter=filter).count()
+ >>> mozilla.specifications(None, filter=filter).count()
5
We can filter for specifications that contain specific text:
- >>> for spec in mozilla.specifications(filter=['install']):
+ >>> for spec in mozilla.specifications(None, filter=['install']):
... print spec.name
extension-manager-upgrades
@@ -178,7 +178,7 @@
Inactive products are excluded from the listings.
>>> filter = [SpecificationFilter.INCOMPLETE]
- >>> mozilla.specifications(filter=filter).count()
+ >>> mozilla.specifications(None, filter=filter).count()
5
>>> from lp.registry.interfaces.product import IProductSet
@@ -190,7 +190,7 @@
>>> firefox.active = False
>>> flush_database_updates()
>>> filter = [SpecificationFilter.INCOMPLETE]
- >>> mozilla.specifications(filter=filter).count()
+ >>> mozilla.specifications(None, filter=filter).count()
0
Reset firefox so we don't mess up later tests.
@@ -248,7 +248,8 @@
mozilla_1_0_series._all_specifications.
>>> filter = [SpecificationFilter.INFORMATIONAL]
- >>> extension_manager_upgrades = mozilla.specifications(filter=filter)[0]
+ >>> extension_manager_upgrades = mozilla.specifications(
+ ... None, filter=filter)[0]
>>> series_1_0 = firefox.getSeries('1.0')
>>> extension_manager_upgrades.proposeGoal(series_1_0, no_priv)
>>> for spec in mozilla_series_1_0._all_specifications:
@@ -264,7 +265,7 @@
Filtered lists of project series related specifications are generated
the same way as for project related specifications.
- >>> for spec in mozilla_series_1_0.specifications(filter=filter):
+ >>> for spec in mozilla_series_1_0.specifications(None, filter=filter):
... print spec.name
extension-manager-upgrades
@@ -277,7 +278,7 @@
project itself.
>>> filter = [SpecificationFilter.INCOMPLETE]
- >>> for spec in mozilla_series_1_0.specifications(filter=filter):
+ >>> for spec in mozilla_series_1_0.specifications(None, filter=filter):
... print spec.name
svg-support
canvas
@@ -287,21 +288,22 @@
Searching for text is also possible.
- >>> for spec in mozilla_series_1_0.specifications(filter=['install']):
+ >>> for spec in mozilla_series_1_0.specifications(
+ ... None, filter=['install']):
... print spec.name
extension-manager-upgrades
Inactive products are excluded from the series listings.
>>> filter = [SpecificationFilter.INCOMPLETE]
- >>> specs = mozilla_series_1_0.specifications(filter=filter)
+ >>> specs = mozilla_series_1_0.specifications(None, filter=filter)
>>> print specs.count()
5
>>> firefox = getUtility(IProductSet).getByName('firefox')
>>> firefox.active = False
>>> filter = [SpecificationFilter.INCOMPLETE]
- >>> mozilla_series_1_0.specifications(filter=filter).count()
+ >>> mozilla_series_1_0.specifications(None, filter=filter).count()
0
Reset firefox so we don't mess up later tests.
=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py 2012-09-27 20:24:22 +0000
+++ lib/lp/registry/model/distribution.py 2012-09-27 20:24:22 +0000
@@ -878,7 +878,7 @@
return getUtility(IDistributionSet).getCurrentSourceReleases(
{self: source_package_names})
- def specifications(self, sort=None, quantity=None, filter=None,
+ def specifications(self, user, sort=None, quantity=None, filter=None,
prejoin_people=True):
"""See `IHasSpecifications`.
=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py 2012-09-27 20:24:22 +0000
+++ lib/lp/registry/model/distroseries.py 2012-09-27 20:24:22 +0000
@@ -777,7 +777,7 @@
"""See `IHasBugs`."""
return self.distribution.official_bug_tags
- def specifications(self, sort=None, quantity=None, filter=None,
+ def specifications(self, user, sort=None, quantity=None, filter=None,
prejoin_people=True):
"""See IHasSpecifications.
=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py 2012-09-27 20:24:22 +0000
+++ lib/lp/registry/model/person.py 2012-09-27 20:24:22 +0000
@@ -821,7 +821,7 @@
"""See `IPerson`."""
return "%s (%s)" % (self.displayname, self.name)
- def specifications(self, sort=None, quantity=None, filter=None,
+ def specifications(self, user, sort=None, quantity=None, filter=None,
prejoin_people=True):
"""See `IHasSpecifications`."""
=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py 2012-09-27 20:24:22 +0000
+++ lib/lp/registry/model/product.py 2012-09-27 20:24:22 +0000
@@ -1303,7 +1303,7 @@
# automatically shared.
return True
- def specifications(self, sort=None, quantity=None, filter=None,
+ def specifications(self, user, sort=None, quantity=None, filter=None,
prejoin_people=True):
"""See `IHasSpecifications`."""
=== modified file 'lib/lp/registry/model/productseries.py'
--- lib/lp/registry/model/productseries.py 2012-09-27 20:24:22 +0000
+++ lib/lp/registry/model/productseries.py 2012-09-27 20:24:22 +0000
@@ -301,7 +301,7 @@
"""See `IProductSeries`."""
return self == self.product.development_focus
- def specifications(self, sort=None, quantity=None, filter=None,
+ def specifications(self, user, sort=None, quantity=None, filter=None,
prejoin_people=True):
"""See IHasSpecifications.
=== modified file 'lib/lp/registry/model/projectgroup.py'
--- lib/lp/registry/model/projectgroup.py 2012-09-27 20:24:22 +0000
+++ lib/lp/registry/model/projectgroup.py 2012-09-27 20:24:22 +0000
@@ -231,7 +231,7 @@
""" % sqlvalues(self, SprintSpecificationStatus.ACCEPTED)
return query, ['Product', 'Specification', 'SprintSpecification']
- def specifications(self, sort=None, quantity=None, filter=None,
+ def specifications(self, user, sort=None, quantity=None, filter=None,
series=None, prejoin_people=True):
"""See `IHasSpecifications`."""
@@ -631,10 +631,11 @@
self.project = project
self.name = name
- def specifications(self, sort=None, quantity=None, filter=None,
+ def specifications(self, user, sort=None, quantity=None, filter=None,
prejoin_people=True):
return self.project.specifications(
- sort, quantity, filter, self.name, prejoin_people=prejoin_people)
+ user, sort, quantity, filter, self.name,
+ prejoin_people=prejoin_people)
@property
def title(self):
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2012-09-22 23:58:54 +0000
+++ lib/lp/testing/factory.py 2012-09-27 20:24:22 +0000
@@ -2091,8 +2091,16 @@
:param product: The product to make the blueprint on. If one is
not specified, an arbitrary product is created.
"""
+ proprietary = (information_type not in PUBLIC_INFORMATION_TYPES and
+ information_type is not None)
if distribution is None and product is None:
- product = self.makeProduct()
+ if proprietary:
+ specification_sharing_policy = (
+ SpecificationSharingPolicy.EMBARGOED_OR_PROPRIETARY)
+ else:
+ specification_sharing_policy = None
+ product = self.makeProduct(
+ specification_sharing_policy=specification_sharing_policy)
if name is None:
name = self.getUniqueString('name')
if summary is None:
@@ -2125,7 +2133,7 @@
priority=priority)
naked_spec = removeSecurityProxy(spec)
if information_type is not None:
- if information_type not in PUBLIC_INFORMATION_TYPES:
+ if proprietary:
naked_spec.target._ensurePolicies([information_type])
naked_spec.transitionToInformationType(
information_type, spec.target.owner)
@@ -4340,9 +4348,9 @@
return link
def makeAccessArtifactGrant(self, artifact=None, grantee=None,
- grantor=None):
+ grantor=None, concrete_artifact=None):
if artifact is None:
- artifact = self.makeAccessArtifact()
+ artifact = self.makeAccessArtifact(concrete_artifact)
if grantee is None:
grantee = self.makePerson()
if grantor is None:
Follow ups