Merge lp:~abentley/launchpad/hide-sprint-blueprints into lp:launchpad

Proposed by Aaron Bentley
Status: Merged
Approved by: j.c.sackett
Approved revision: no longer in the source branch.
Merged at revision: 16057
Proposed branch: lp:~abentley/launchpad/hide-sprint-blueprints
Merge into: lp:launchpad
Prerequisite: lp:~abentley/launchpad/specification-cleanup
Diff against target: 1182 lines (+193/-119)
29 files modified
lib/lp/app/browser/root.py (+1/-1)
lib/lp/blueprints/browser/specification.py (+4/-4)
lib/lp/blueprints/browser/specificationtarget.py (+7/-6)
lib/lp/blueprints/browser/sprint.py (+2/-2)
lib/lp/blueprints/browser/tests/test_sprint.py (+13/-0)
lib/lp/blueprints/doc/specification.txt (+2/-2)
lib/lp/blueprints/doc/sprint.txt (+6/-6)
lib/lp/blueprints/interfaces/specification.py (+1/-1)
lib/lp/blueprints/interfaces/specificationtarget.py (+2/-1)
lib/lp/blueprints/model/specification.py (+10/-6)
lib/lp/blueprints/model/sprint.py (+14/-7)
lib/lp/blueprints/model/tests/test_sprint.py (+47/-14)
lib/lp/blueprints/templates/person-specworkload.pt (+1/-1)
lib/lp/blueprints/vocabularies/specification.py (+2/-1)
lib/lp/registry/browser/person.py (+5/-2)
lib/lp/registry/doc/distribution.txt (+8/-8)
lib/lp/registry/doc/distroseries.txt (+9/-9)
lib/lp/registry/doc/person-account.txt (+1/-1)
lib/lp/registry/doc/person.txt (+9/-9)
lib/lp/registry/doc/product.txt (+4/-4)
lib/lp/registry/doc/productseries.txt (+10/-10)
lib/lp/registry/doc/projectgroup.txt (+14/-12)
lib/lp/registry/model/distribution.py (+1/-1)
lib/lp/registry/model/distroseries.py (+1/-1)
lib/lp/registry/model/person.py (+1/-1)
lib/lp/registry/model/product.py (+1/-1)
lib/lp/registry/model/productseries.py (+1/-1)
lib/lp/registry/model/projectgroup.py (+4/-3)
lib/lp/testing/factory.py (+12/-4)
To merge this branch: bzr merge lp:~abentley/launchpad/hide-sprint-blueprints
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+126792@code.launchpad.net

Commit message

Respect privacy listing specifications for sprints.

Description of the change

= 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.

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

This looks alright.

As one minor point, it might be cleaner to set the default argument for user in `specifications` to None, rather than passing in None everytime you need it. But I don't see that as a blocker to approval--it's just a thought.

review: Approve
Revision history for this message
Aaron Bentley (abentley) wrote :

> As one minor point, it might be cleaner to set the default argument for user
> in `specifications` to None, rather than passing in None everytime you need
> it. But I don't see that as a blocker to approval--it's just a thought.

I don't think it would be cleaner to set a default, because it would encourage mistakes and sloppy use. The fact is that you need to decide what user to use in all cases. What's worse, mistakes will cause only subtle differences and may go undetected. Deciding to use None is valid, but in most cases you'll be passing in a variable that may or may not have been set to None, like ILaunchBag.user or LaunchpadView.user.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/app/browser/root.py'
--- lib/lp/app/browser/root.py 2012-09-28 14:42:25 +0000
+++ lib/lp/app/browser/root.py 2012-09-28 14:42:25 +0000
@@ -130,7 +130,7 @@
130 @property130 @property
131 def blueprint_count(self):131 def blueprint_count(self):
132 """The total blueprint count in all of Launchpad."""132 """The total blueprint count in all of Launchpad."""
133 return getUtility(ISpecificationSet).specificationCount()133 return getUtility(ISpecificationSet).specificationCount(self.user)
134134
135 @property135 @property
136 def answer_count(self):136 def answer_count(self):
137137
=== modified file 'lib/lp/blueprints/browser/specification.py'
--- lib/lp/blueprints/browser/specification.py 2012-09-28 14:42:25 +0000
+++ lib/lp/blueprints/browser/specification.py 2012-09-28 14:42:25 +0000
@@ -1036,7 +1036,7 @@
1036 """Override the setup to define own fields."""1036 """Override the setup to define own fields."""
1037 if self.context.target is None:1037 if self.context.target is None:
1038 raise AssertionError("No target found for this spec.")1038 raise AssertionError("No target found for this spec.")
1039 specs = sorted(self.context.target.specifications(),1039 specs = sorted(self.context.target.specifications(self.user),
1040 key=attrgetter('name'))1040 key=attrgetter('name'))
1041 terms = [SimpleTerm(spec, spec.name, spec.title)1041 terms = [SimpleTerm(spec, spec.name, spec.title)
1042 for spec in specs if spec != self.context]1042 for spec in specs if spec != self.context]
@@ -1530,17 +1530,17 @@
1530 @property1530 @property
1531 def latest_specifications(self):1531 def latest_specifications(self):
1532 return self.context.specifications(1532 return self.context.specifications(
1533 sort=SpecificationSort.DATE, quantity=5)1533 self.user, sort=SpecificationSort.DATE, quantity=5)
15341534
1535 @property1535 @property
1536 def latest_completed_specifications(self):1536 def latest_completed_specifications(self):
1537 return self.context.specifications(1537 return self.context.specifications(
1538 sort=SpecificationSort.DATE, quantity=5,1538 self.user, sort=SpecificationSort.DATE, quantity=5,
1539 filter=[SpecificationFilter.COMPLETE])1539 filter=[SpecificationFilter.COMPLETE])
15401540
1541 @property1541 @property
1542 def specification_count(self):1542 def specification_count(self):
1543 return self.context.specificationCount()1543 return self.context.specificationCount(self.user)
15441544
1545 @safe_action1545 @safe_action
1546 @action('Find blueprints', name="search")1546 @action('Find blueprints', name="search")
15471547
=== modified file 'lib/lp/blueprints/browser/specificationtarget.py'
--- lib/lp/blueprints/browser/specificationtarget.py 2012-09-28 14:42:25 +0000
+++ lib/lp/blueprints/browser/specificationtarget.py 2012-09-28 14:42:25 +0000
@@ -259,11 +259,11 @@
259259
260 @cachedproperty260 @cachedproperty
261 def has_any_specifications(self):261 def has_any_specifications(self):
262 return self.context._all_specifications.count() != 0262 return not self.context._all_specifications.is_empty()
263263
264 @cachedproperty264 @cachedproperty
265 def all_specifications(self):265 def all_specifications(self):
266 return shortlist(self.context.all_specifications)266 return shortlist(self.context.all_specifications(self.user))
267267
268 @cachedproperty268 @cachedproperty
269 def searchrequested(self):269 def searchrequested(self):
@@ -348,7 +348,7 @@
348 and not check_permission('launchpad.View', self.context)):348 and not check_permission('launchpad.View', self.context)):
349 return []349 return []
350 filter = self.spec_filter350 filter = self.spec_filter
351 return self.context.specifications(filter=filter)351 return self.context.specifications(self.user, filter=filter)
352352
353 @cachedproperty353 @cachedproperty
354 def specs_batched(self):354 def specs_batched(self):
@@ -364,7 +364,7 @@
364 def documentation(self):364 def documentation(self):
365 filter = [SpecificationFilter.COMPLETE,365 filter = [SpecificationFilter.COMPLETE,
366 SpecificationFilter.INFORMATIONAL]366 SpecificationFilter.INFORMATIONAL]
367 return shortlist(self.context.specifications(filter=filter))367 return shortlist(self.context.specifications(self.user, filter=filter))
368368
369 @cachedproperty369 @cachedproperty
370 def categories(self):370 def categories(self):
@@ -405,8 +405,9 @@
405 Only ACCEPTED specifications are returned. This list is used by the405 Only ACCEPTED specifications are returned. This list is used by the
406 +portlet-latestspecs view.406 +portlet-latestspecs view.
407 """407 """
408 return self.context.specifications(sort=SpecificationSort.DATE,408 return self.context.specifications(self.user,
409 quantity=quantity, prejoin_people=False)409 sort=SpecificationSort.DATE, quantity=quantity,
410 prejoin_people=False)
410411
411412
412class SpecificationAssignmentsView(HasSpecificationsView):413class SpecificationAssignmentsView(HasSpecificationsView):
413414
=== 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-28 14:42:25 +0000
@@ -216,7 +216,7 @@
216 @cachedproperty216 @cachedproperty
217 def latest_approved(self):217 def latest_approved(self):
218 filter = [SpecificationFilter.ACCEPTED]218 filter = [SpecificationFilter.ACCEPTED]
219 return self.context.specifications(filter=filter,219 return self.context.specifications(self.user, filter=filter,
220 quantity=self.latest_specs_limit,220 quantity=self.latest_specs_limit,
221 sort=SpecificationSort.DATE)221 sort=SpecificationSort.DATE)
222222
@@ -464,7 +464,7 @@
464464
465 model_specs = []465 model_specs = []
466 for spec in self.context.specifications(466 for spec in self.context.specifications(
467 filter=[SpecificationFilter.ACCEPTED]):467 self.user, filter=[SpecificationFilter.ACCEPTED]):
468468
469 # skip sprints with no priority or less than low:469 # skip sprints with no priority or less than low:
470 if spec.priority < SpecificationPriority.UNDEFINED:470 if spec.priority < SpecificationPriority.UNDEFINED:
471471
=== modified file 'lib/lp/blueprints/browser/tests/test_sprint.py'
--- lib/lp/blueprints/browser/tests/test_sprint.py 2012-09-28 14:42:25 +0000
+++ lib/lp/blueprints/browser/tests/test_sprint.py 2012-09-28 14:42:25 +0000
@@ -8,6 +8,7 @@
8from storm.locals import Store8from storm.locals import Store
9from testtools.matchers import Equals9from testtools.matchers import Equals
1010
11from lp.app.enums import InformationType
11from lp.testing import BrowserTestCase12from lp.testing import BrowserTestCase
12from lp.testing.layers import DatabaseFunctionalLayer13from lp.testing.layers import DatabaseFunctionalLayer
13from lp.testing.matchers import BrowsesWithQueryLimit, HasQueryCount14from lp.testing.matchers import BrowsesWithQueryLimit, HasQueryCount
@@ -40,3 +41,15 @@
40 with QueryCollector() as recorder:41 with QueryCollector() as recorder:
41 self.getViewBrowser(sprint)42 self.getViewBrowser(sprint)
42 self.assertThat(recorder, HasQueryCount(Equals(30)))43 self.assertThat(recorder, HasQueryCount(Equals(30)))
44
45 def test_proprietary_blueprint_listing_query_count(self):
46 """Set a maximum number of queries for sprint blueprint lists."""
47 sprint = self.factory.makeSprint()
48 for count in range(10):
49 blueprint = self.factory.makeSpecification(
50 information_type=InformationType.PROPRIETARY)
51 link = blueprint.linkSprint(sprint, blueprint.owner)
52 link.acceptBy(sprint.owner)
53 with QueryCollector() as recorder:
54 self.getViewBrowser(sprint)
55 self.assertThat(recorder, HasQueryCount(Equals(22)))
4356
=== modified file 'lib/lp/blueprints/doc/specification.txt'
--- lib/lp/blueprints/doc/specification.txt 2012-09-28 14:42:25 +0000
+++ lib/lp/blueprints/doc/specification.txt 2012-09-28 14:42:25 +0000
@@ -222,7 +222,7 @@
222We can filter for specifications that contain specific text, across all222We can filter for specifications that contain specific text, across all
223specifications:223specifications:
224224
225 >>> for spec in specset.specifications(filter=['install']):225 >>> for spec in specset.specifications(None, filter=['install']):
226 ... print spec.name, spec.target.name226 ... print spec.name, spec.target.name
227 cluster-installation kubuntu227 cluster-installation kubuntu
228 extension-manager-upgrades firefox228 extension-manager-upgrades firefox
@@ -239,7 +239,7 @@
239 >>> unlink_source_packages(upstream_firefox)239 >>> unlink_source_packages(upstream_firefox)
240 >>> upstream_firefox.active = False240 >>> upstream_firefox.active = False
241 >>> flush_database_updates()241 >>> flush_database_updates()
242 >>> for spec in specset.specifications(filter=['install']):242 >>> for spec in specset.specifications(None, filter=['install']):
243 ... print spec.name, spec.target.name243 ... print spec.name, spec.target.name
244 cluster-installation kubuntu244 cluster-installation kubuntu
245 media-integrity-check ubuntu245 media-integrity-check ubuntu
246246
=== modified file 'lib/lp/blueprints/doc/sprint.txt'
--- lib/lp/blueprints/doc/sprint.txt 2011-12-30 06:14:56 +0000
+++ lib/lp/blueprints/doc/sprint.txt 2012-09-28 14:42:25 +0000
@@ -118,19 +118,19 @@
118First, there should be no informational specs for ubz:118First, there should be no informational specs for ubz:
119119
120 >>> filter = [SpecificationFilter.INFORMATIONAL]120 >>> filter = [SpecificationFilter.INFORMATIONAL]
121 >>> ubz.specifications(filter=filter).count()121 >>> ubz.specifications(None, filter=filter).count()
122 1122 1
123123
124There are 0 completed specs for UBZ:124There are 0 completed specs for UBZ:
125125
126 >>> filter = [SpecificationFilter.COMPLETE]126 >>> filter = [SpecificationFilter.COMPLETE]
127 >>> ubz.specifications(filter=filter).count()127 >>> ubz.specifications(None, filter=filter).count()
128 0128 0
129129
130And there are three incomplete specs:130And there are three incomplete specs:
131131
132 >>> filter = [SpecificationFilter.INCOMPLETE]132 >>> filter = [SpecificationFilter.INCOMPLETE]
133 >>> for spec in ubz.specifications(filter=filter):133 >>> for spec in ubz.specifications(None, filter=filter):
134 ... print spec.name, spec.is_complete134 ... print spec.name, spec.is_complete
135 svg-support False135 svg-support False
136 extension-manager-upgrades False136 extension-manager-upgrades False
@@ -139,7 +139,7 @@
139If we ask for all specs, we get them in the order of priority.139If we ask for all specs, we get them in the order of priority.
140140
141 >>> filter = [SpecificationFilter.ALL]141 >>> filter = [SpecificationFilter.ALL]
142 >>> for spec in ubz.specifications(filter=filter):142 >>> for spec in ubz.specifications(None, filter=filter):
143 ... print spec.priority.title, spec.name143 ... print spec.priority.title, spec.name
144 High svg-support144 High svg-support
145 Medium extension-manager-upgrades145 Medium extension-manager-upgrades
@@ -147,7 +147,7 @@
147147
148And if we ask just for specs, we get them all148And if we ask just for specs, we get them all
149149
150 >>> for spec in ubz.specifications():150 >>> for spec in ubz.specifications(None):
151 ... print spec.name, spec.is_complete151 ... print spec.name, spec.is_complete
152 svg-support False152 svg-support False
153 extension-manager-upgrades False153 extension-manager-upgrades False
@@ -167,7 +167,7 @@
167 >>> unlink_source_packages(firefox)167 >>> unlink_source_packages(firefox)
168 >>> firefox.active = False168 >>> firefox.active = False
169 >>> flush_database_updates()169 >>> flush_database_updates()
170 >>> ubz.specifications().count()170 >>> ubz.specifications(None).count()
171 0171 0
172172
173Reset firefox so we don't mess up later tests.173Reset firefox so we don't mess up later tests.
174174
=== modified file 'lib/lp/blueprints/interfaces/specification.py'
--- lib/lp/blueprints/interfaces/specification.py 2012-09-28 14:42:25 +0000
+++ lib/lp/blueprints/interfaces/specification.py 2012-09-28 14:42:25 +0000
@@ -674,7 +674,7 @@
674674
675 coming_sprints = Attribute("The next 5 sprints in the system.")675 coming_sprints = Attribute("The next 5 sprints in the system.")
676676
677 def specificationCount():677 def specificationCount(user):
678 """The total number of blueprints in Launchpad"""678 """The total number of blueprints in Launchpad"""
679679
680 def getStatusCountsForProductSeries(product_series):680 def getStatusCountsForProductSeries(product_series):
681681
=== modified file 'lib/lp/blueprints/interfaces/specificationtarget.py'
--- lib/lp/blueprints/interfaces/specificationtarget.py 2012-09-28 14:42:25 +0000
+++ lib/lp/blueprints/interfaces/specificationtarget.py 2012-09-28 14:42:25 +0000
@@ -60,10 +60,11 @@
60 'have not been accepted for that goal'))),60 'have not been accepted for that goal'))),
61 exported_as="valid_specifications", as_of="devel")61 exported_as="valid_specifications", as_of="devel")
6262
63 def specifications(quantity=None, sort=None, filter=None,63 def specifications(user, quantity=None, sort=None, filter=None,
64 prejoin_people=True):64 prejoin_people=True):
65 """Specifications for this target.65 """Specifications for this target.
6666
67 The user specifies which user to use for calculation of visibility.
67 The sort is a dbschema which indicates the preferred sort order. The68 The sort is a dbschema which indicates the preferred sort order. The
68 filter is an indicator of the kinds of specs to be returned, and69 filter is an indicator of the kinds of specs to be returned, and
69 appropriate filters depend on the kind of object this method is on.70 appropriate filters depend on the kind of object this method is on.
7071
=== modified file 'lib/lp/blueprints/model/specification.py'
--- lib/lp/blueprints/model/specification.py 2012-09-28 14:42:25 +0000
+++ lib/lp/blueprints/model/specification.py 2012-09-28 14:42:25 +0000
@@ -109,6 +109,7 @@
109 cachedproperty,109 cachedproperty,
110 get_property_cache,110 get_property_cache,
111 )111 )
112from lp.services.webapp.interfaces import ILaunchBag
112113
113114
114def recursive_blocked_query(spec):115def recursive_blocked_query(spec):
@@ -957,7 +958,7 @@
957 for other classes that have specifications.958 for other classes that have specifications.
958 """959 """
959960
960 def specifications(self, sort=None, quantity=None, filter=None,961 def specifications(self, user, sort=None, quantity=None, filter=None,
961 prejoin_people=True):962 prejoin_people=True):
962 """See IHasSpecifications."""963 """See IHasSpecifications."""
963 # this should be implemented by the actual context class964 # this should be implemented by the actual context class
@@ -1024,16 +1025,19 @@
1024 @property1025 @property
1025 def _all_specifications(self):1026 def _all_specifications(self):
1026 """See IHasSpecifications."""1027 """See IHasSpecifications."""
1027 return self.specifications(filter=[SpecificationFilter.ALL])1028 user = getUtility(ILaunchBag).user
1029 return self.specifications(user, filter=[SpecificationFilter.ALL])
10281030
1029 @property1031 @property
1030 def _valid_specifications(self):1032 def _valid_specifications(self):
1031 """See IHasSpecifications."""1033 """See IHasSpecifications."""
1032 return self.specifications(filter=[SpecificationFilter.VALID])1034 user = getUtility(ILaunchBag).user
1035 return self.specifications(user, filter=[SpecificationFilter.VALID])
10331036
1034 def specificationCount(self):1037 def specificationCount(self, user):
1035 """See IHasSpecifications."""1038 """See IHasSpecifications."""
1036 return self.specifications(filter=[SpecificationFilter.ALL]).count()1039 return self.specifications(user,
1040 filter=[SpecificationFilter.ALL]).count()
10371041
10381042
1039class SpecificationSet(HasSpecificationsMixin):1043class SpecificationSet(HasSpecificationsMixin):
@@ -1072,7 +1076,7 @@
1072 """See ISpecificationSet."""1076 """See ISpecificationSet."""
1073 return iter(self.all_specifications)1077 return iter(self.all_specifications)
10741078
1075 def specifications(self, sort=None, quantity=None, filter=None,1079 def specifications(self, user, sort=None, quantity=None, filter=None,
1076 prejoin_people=True):1080 prejoin_people=True):
1077 """See IHasSpecifications."""1081 """See IHasSpecifications."""
10781082
10791083
=== modified file 'lib/lp/blueprints/model/sprint.py'
--- lib/lp/blueprints/model/sprint.py 2012-09-28 14:42:25 +0000
+++ lib/lp/blueprints/model/sprint.py 2012-09-28 14:42:25 +0000
@@ -41,7 +41,10 @@
41 ISprint,41 ISprint,
42 ISprintSet,42 ISprintSet,
43 )43 )
44from lp.blueprints.model.specification import HasSpecificationsMixin44from lp.blueprints.model.specification import (
45 get_specification_privacy_filter,
46 HasSpecificationsMixin,
47 )
45from lp.blueprints.model.sprintattendance import SprintAttendance48from lp.blueprints.model.sprintattendance import SprintAttendance
46from lp.blueprints.model.sprintspecification import SprintSpecification49from lp.blueprints.model.sprintspecification import SprintSpecification
47from lp.registry.interfaces.person import (50from lp.registry.interfaces.person import (
@@ -111,7 +114,7 @@
111 # Only really used in tests.114 # Only really used in tests.
112 return [a.attendee for a in self.attendances]115 return [a.attendee for a in self.attendances]
113116
114 def spec_filter_clause(self, filter=None):117 def spec_filter_clause(self, user, filter=None):
115 """Figure out the appropriate query for specifications on a sprint.118 """Figure out the appropriate query for specifications on a sprint.
116119
117 We separate out the query generation from the normal120 We separate out the query generation from the normal
@@ -126,6 +129,7 @@
126 Or(Specification.product == None,129 Or(Specification.product == None,
127 Not(Specification.productID.is_in(Select(Product.id,130 Not(Specification.productID.is_in(Select(Product.id,
128 Product.active == False))))]131 Product.active == False))))]
132 query.append(get_specification_privacy_filter(user))
129 if not filter:133 if not filter:
130 # filter could be None or [] then we decide the default134 # filter could be None or [] then we decide the default
131 # which for a sprint is to show everything approved135 # which for a sprint is to show everything approved
@@ -169,7 +173,10 @@
169 query.append(fti_search(Specification, constraint))173 query.append(fti_search(Specification, constraint))
170 return query174 return query
171175
172 def specifications(self, sort=None, quantity=None, filter=None,176 def all_specifications(self, user):
177 return self.specifications(user, filter=[SpecificationFilter.ALL])
178
179 def specifications(self, user, sort=None, quantity=None, filter=None,
173 prejoin_people=False):180 prejoin_people=False):
174 """See IHasSpecifications."""181 """See IHasSpecifications."""
175 # prejoin_people is provided only for interface compatibility and182 # prejoin_people is provided only for interface compatibility and
@@ -177,7 +184,7 @@
177 assert not prejoin_people184 assert not prejoin_people
178 if filter is None:185 if filter is None:
179 filter = set([SpecificationFilter.ACCEPTED])186 filter = set([SpecificationFilter.ACCEPTED])
180 query = self.spec_filter_clause(filter=filter)187 query = self.spec_filter_clause(user, filter=filter)
181 # import here to avoid circular deps188 # import here to avoid circular deps
182 from lp.blueprints.model.specification import Specification189 from lp.blueprints.model.specification import Specification
183 results = Store.of(self).find(Specification, *query)190 results = Store.of(self).find(Specification, *query)
@@ -200,7 +207,7 @@
200207
201 def specificationLinks(self, filter=None):208 def specificationLinks(self, filter=None):
202 """See `ISprint`."""209 """See `ISprint`."""
203 query = self.spec_filter_clause(filter=filter)210 query = self.spec_filter_clause(None, filter=filter)
204 result = Store.of(self).find(SprintSpecification, *query)211 result = Store.of(self).find(SprintSpecification, *query)
205 return result212 return result
206213
@@ -227,7 +234,7 @@
227 # queue234 # queue
228 flush_database_updates()235 flush_database_updates()
229236
230 return self.specifications(237 return self.specifications(decider,
231 filter=[SpecificationFilter.PROPOSED]).count()238 filter=[SpecificationFilter.PROPOSED]).count()
232239
233 def declineSpecificationLinks(self, idlist, decider):240 def declineSpecificationLinks(self, idlist, decider):
@@ -241,7 +248,7 @@
241 # queue248 # queue
242 flush_database_updates()249 flush_database_updates()
243250
244 return self.specifications(251 return self.specifications(decider,
245 filter=[SpecificationFilter.PROPOSED]).count()252 filter=[SpecificationFilter.PROPOSED]).count()
246253
247 # attendance254 # attendance
248255
=== modified file 'lib/lp/blueprints/model/tests/test_sprint.py'
--- lib/lp/blueprints/model/tests/test_sprint.py 2012-09-28 14:42:25 +0000
+++ lib/lp/blueprints/model/tests/test_sprint.py 2012-09-28 14:42:25 +0000
@@ -9,8 +9,10 @@
9import datetime9import datetime
1010
11from pytz import utc11from pytz import utc
12from zope.component import getUtility
12from zope.security.proxy import removeSecurityProxy13from zope.security.proxy import removeSecurityProxy
1314
15from lp.app.enums import InformationType
14from lp.blueprints.enums import (16from lp.blueprints.enums import (
15 NewSpecificationDefinitionStatus,17 NewSpecificationDefinitionStatus,
16 SpecificationDefinitionStatus,18 SpecificationDefinitionStatus,
@@ -18,12 +20,13 @@
18 SpecificationPriority,20 SpecificationPriority,
19 SpecificationSort,21 SpecificationSort,
20 )22 )
23from lp.registry.interfaces.accesspolicy import IAccessPolicySource
21from lp.testing import TestCaseWithFactory24from lp.testing import TestCaseWithFactory
22from lp.testing.layers import DatabaseFunctionalLayer25from lp.testing.layers import DatabaseFunctionalLayer
2326
2427
25def list_result(sprint, filter=None):28def list_result(sprint, filter=None, user=None):
26 result = sprint.specifications(SpecificationSort.DATE, filter=filter)29 result = sprint.specifications(user, SpecificationSort.DATE, filter=filter)
27 return list(result)30 return list(result)
2831
2932
@@ -38,11 +41,12 @@
38 def makeSpec(self, sprint=None, date_decided=0, date_created=0,41 def makeSpec(self, sprint=None, date_decided=0, date_created=0,
39 proposed=False, declined=False, title=None,42 proposed=False, declined=False, title=None,
40 status=NewSpecificationDefinitionStatus.NEW,43 status=NewSpecificationDefinitionStatus.NEW,
41 name=None, priority=None):44 name=None, priority=None, information_type=None):
42 if sprint is None:45 if sprint is None:
43 sprint = self.factory.makeSprint()46 sprint = self.factory.makeSprint()
44 blueprint = self.factory.makeSpecification(47 blueprint = self.factory.makeSpecification(
45 title=title, status=status, name=name, priority=priority)48 title=title, status=status, name=name, priority=priority,
49 information_type=information_type)
46 link = blueprint.linkSprint(sprint, blueprint.owner)50 link = blueprint.linkSprint(sprint, blueprint.owner)
47 naked_link = removeSecurityProxy(link)51 naked_link = removeSecurityProxy(link)
48 if declined:52 if declined:
@@ -61,10 +65,11 @@
61 sprint = self.factory.makeSprint()65 sprint = self.factory.makeSprint()
62 for count in range(10):66 for count in range(10):
63 self.makeSpec(sprint)67 self.makeSpec(sprint)
64 self.assertEqual(10, sprint.specifications().count())68 self.assertEqual(10, sprint.specifications(None).count())
65 self.assertEqual(10, sprint.specifications(quantity=None).count())69 result = sprint.specifications(None, quantity=None).count()
66 self.assertEqual(8, sprint.specifications(quantity=8).count())70 self.assertEqual(10, result)
67 self.assertEqual(10, sprint.specifications(quantity=11).count())71 self.assertEqual(8, sprint.specifications(None, quantity=8).count())
72 self.assertEqual(10, sprint.specifications(None, quantity=11).count())
6873
69 def test_specifications_date_sort_accepted_decided(self):74 def test_specifications_date_sort_accepted_decided(self):
70 # If only accepted proposals are requested, date-sorting uses75 # If only accepted proposals are requested, date-sorting uses
@@ -133,9 +138,9 @@
133 blueprint3 = self.makeSpec(138 blueprint3 = self.makeSpec(
134 sprint, priority=SpecificationPriority.LOW,139 sprint, priority=SpecificationPriority.LOW,
135 status=SpecificationDefinitionStatus.OBSOLETE)140 status=SpecificationDefinitionStatus.OBSOLETE)
136 result = sprint.specifications()141 result = sprint.specifications(None)
137 self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))142 self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
138 result = sprint.specifications(sort=SpecificationSort.PRIORITY)143 result = sprint.specifications(None, sort=SpecificationSort.PRIORITY)
139 self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))144 self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
140145
141 def test_priority_sort_fallback_status(self):146 def test_priority_sort_fallback_status(self):
@@ -148,9 +153,9 @@
148 sprint, status=SpecificationDefinitionStatus.APPROVED, name='c')153 sprint, status=SpecificationDefinitionStatus.APPROVED, name='c')
149 blueprint3 = self.makeSpec(154 blueprint3 = self.makeSpec(
150 sprint, status=SpecificationDefinitionStatus.NEW, name='b')155 sprint, status=SpecificationDefinitionStatus.NEW, name='b')
151 result = sprint.specifications()156 result = sprint.specifications(None)
152 self.assertEqual([blueprint2, blueprint3, blueprint1], list(result))157 self.assertEqual([blueprint2, blueprint3, blueprint1], list(result))
153 result = sprint.specifications(sort=SpecificationSort.PRIORITY)158 result = sprint.specifications(None, sort=SpecificationSort.PRIORITY)
154 self.assertEqual([blueprint2, blueprint3, blueprint1], list(result))159 self.assertEqual([blueprint2, blueprint3, blueprint1], list(result))
155160
156 def test_priority_sort_fallback_name(self):161 def test_priority_sort_fallback_name(self):
@@ -159,9 +164,9 @@
159 sprint = blueprint1.sprints[0]164 sprint = blueprint1.sprints[0]
160 blueprint2 = self.makeSpec(sprint, name='c')165 blueprint2 = self.makeSpec(sprint, name='c')
161 blueprint3 = self.makeSpec(sprint, name='a')166 blueprint3 = self.makeSpec(sprint, name='a')
162 result = sprint.specifications()167 result = sprint.specifications(None)
163 self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))168 self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
164 result = sprint.specifications(sort=SpecificationSort.PRIORITY)169 result = sprint.specifications(None, sort=SpecificationSort.PRIORITY)
165 self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))170 self.assertEqual([blueprint3, blueprint1, blueprint2], list(result))
166171
167 def test_text_search(self):172 def test_text_search(self):
@@ -182,6 +187,34 @@
182 result = list_result(sprint, [SpecificationFilter.DECLINED])187 result = list_result(sprint, [SpecificationFilter.DECLINED])
183 self.assertEqual([blueprint2], result)188 self.assertEqual([blueprint2], result)
184189
190 def test_proprietary_not_listed(self):
191 # Proprietary blueprints are not listed for random users
192 blueprint1 = self.makeSpec(
193 information_type=InformationType.PROPRIETARY)
194 self.assertEqual([], list_result(blueprint1.sprints[0]))
195
196 def test_proprietary_listed_for_artifact_grant(self):
197 # Proprietary blueprints are listed for users with an artifact grant.
198 blueprint1 = self.makeSpec(
199 information_type=InformationType.PROPRIETARY)
200 grant = self.factory.makeAccessArtifactGrant(
201 concrete_artifact=blueprint1)
202 self.assertEqual(
203 [blueprint1],
204 list_result(blueprint1.sprints[0], user=grant.grantee))
205
206 def test_proprietary_listed_for_policy_grant(self):
207 # Proprietary blueprints are listed for users with a policy grant.
208 blueprint1 = self.makeSpec(
209 information_type=InformationType.PROPRIETARY)
210 policy_source = getUtility(IAccessPolicySource)
211 (policy,) = policy_source.find(
212 [(blueprint1.product, InformationType.PROPRIETARY)])
213 grant = self.factory.makeAccessPolicyGrant(policy)
214 self.assertEqual(
215 [blueprint1],
216 list_result(blueprint1.sprints[0], user=grant.grantee))
217
185218
186class TestSprintAttendancesSort(TestCaseWithFactory):219class TestSprintAttendancesSort(TestCaseWithFactory):
187220
188221
=== 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-28 14:42:25 +0000
@@ -44,7 +44,7 @@
4444
45 <tal:participants repeat="member members_batch">45 <tal:participants repeat="member members_batch">
4646
47 <tal:specs define="specifications member/specifications">47 <tal:specs define="specifications member/@@+specworkload/specifications">
4848
49 <div style="margin-bottom: 1em;" tal:condition="specifications">49 <div style="margin-bottom: 1em;" tal:condition="specifications">
50 <p>50 <p>
5151
=== modified file 'lib/lp/blueprints/vocabularies/specification.py'
--- lib/lp/blueprints/vocabularies/specification.py 2012-01-01 02:58:52 +0000
+++ lib/lp/blueprints/vocabularies/specification.py 2012-09-28 14:42:25 +0000
@@ -39,7 +39,8 @@
3939
40 if target is not None:40 if target is not None:
41 for spec in sorted(41 for spec in sorted(
42 target.specifications(), key=attrgetter('title')):42 target.specifications(launchbag.user),
43 key=attrgetter('title')):
43 # we will not show the current specification in the44 # we will not show the current specification in the
44 # launchbag45 # launchbag
45 if spec == launchbag.specification:46 if spec == launchbag.specification:
4647
=== 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-28 14:42:25 +0000
@@ -1303,6 +1303,9 @@
1303 batch_nav = BatchNavigator(members, self.request, size=20)1303 batch_nav = BatchNavigator(members, self.request, size=20)
1304 return batch_nav1304 return batch_nav
13051305
1306 def specifications(self):
1307 return self.context.specifications(self.user)
1308
13061309
1307class PersonSpecWorkloadTableView(LaunchpadView):1310class PersonSpecWorkloadTableView(LaunchpadView):
1308 """View to render the specification workload table for a person.1311 """View to render the specification workload table for a person.
@@ -1331,7 +1334,7 @@
1331 approver, the assignee or the drafter.1334 approver, the assignee or the drafter.
1332 """1335 """
1333 return [PersonSpecWorkloadTableView.PersonSpec(spec, self.context)1336 return [PersonSpecWorkloadTableView.PersonSpec(spec, self.context)
1334 for spec in self.context.specifications()]1337 for spec in self.context.specifications(self.user)]
13351338
13361339
1337class PersonVouchersView(LaunchpadFormView):1340class PersonVouchersView(LaunchpadFormView):
@@ -3573,7 +3576,7 @@
3573 else:3576 else:
3574 project['bug_count'] = pillar.searchTasks(3577 project['bug_count'] = pillar.searchTasks(
3575 BugTaskSet().open_bugtask_search).count()3578 BugTaskSet().open_bugtask_search).count()
3576 project['spec_count'] = pillar.specifications().count()3579 project['spec_count'] = pillar.specifications(user).count()
3577 project['question_count'] = pillar.searchQuestions().count()3580 project['question_count'] = pillar.searchQuestions().count()
3578 projects.append(project)3581 projects.append(project)
3579 return projects3582 return projects
35803583
=== modified file 'lib/lp/registry/doc/distribution.txt'
--- lib/lp/registry/doc/distribution.txt 2012-09-28 14:42:25 +0000
+++ lib/lp/registry/doc/distribution.txt 2012-09-28 14:42:25 +0000
@@ -490,18 +490,18 @@
490complete so it will not show up unless we explicitly ask for complete specs:490complete so it will not show up unless we explicitly ask for complete specs:
491491
492 >>> filter = [SpecificationFilter.INFORMATIONAL]492 >>> filter = [SpecificationFilter.INFORMATIONAL]
493 >>> kubuntu.specifications(filter=filter).count()493 >>> kubuntu.specifications(None, filter=filter).count()
494 0494 0
495 >>> filter = [SpecificationFilter.INFORMATIONAL,495 >>> filter = [SpecificationFilter.INFORMATIONAL,
496 ... SpecificationFilter.COMPLETE]496 ... SpecificationFilter.COMPLETE]
497 >>> kubuntu.specifications(filter=filter).count()497 >>> kubuntu.specifications(None, filter=filter).count()
498 1498 1
499499
500500
501There are 2 completed specs for Kubuntu:501There are 2 completed specs for Kubuntu:
502502
503 >>> filter = [SpecificationFilter.COMPLETE]503 >>> filter = [SpecificationFilter.COMPLETE]
504 >>> for spec in kubuntu.specifications(filter=filter):504 >>> for spec in kubuntu.specifications(None, filter=filter):
505 ... print spec.name, spec.is_complete505 ... print spec.name, spec.is_complete
506 thinclient-local-devices True506 thinclient-local-devices True
507 usplash-on-hibernation True507 usplash-on-hibernation True
@@ -510,7 +510,7 @@
510And there are four incomplete specs:510And there are four incomplete specs:
511511
512 >>> filter = [SpecificationFilter.INCOMPLETE]512 >>> filter = [SpecificationFilter.INCOMPLETE]
513 >>> for spec in kubuntu.specifications(filter=filter):513 >>> for spec in kubuntu.specifications(None, filter=filter):
514 ... print spec.name, spec.is_complete514 ... print spec.name, spec.is_complete
515 cluster-installation False515 cluster-installation False
516 revu False516 revu False
@@ -521,7 +521,7 @@
521If we ask for all specs, we get them in the order of priority.521If we ask for all specs, we get them in the order of priority.
522522
523 >>> filter = [SpecificationFilter.ALL]523 >>> filter = [SpecificationFilter.ALL]
524 >>> for spec in kubuntu.specifications(filter=filter):524 >>> for spec in kubuntu.specifications(None, filter=filter):
525 ... print spec.priority.title, spec.name525 ... print spec.priority.title, spec.name
526 Essential cluster-installation526 Essential cluster-installation
527 High revu527 High revu
@@ -533,7 +533,7 @@
533533
534And if we ask just for specs, we get the incomplete ones.534And if we ask just for specs, we get the incomplete ones.
535535
536 >>> for spec in kubuntu.specifications():536 >>> for spec in kubuntu.specifications(None):
537 ... print spec.name, spec.is_complete537 ... print spec.name, spec.is_complete
538 cluster-installation False538 cluster-installation False
539 revu False539 revu False
@@ -542,7 +542,7 @@
542542
543We can filter for specifications that contain specific text:543We can filter for specifications that contain specific text:
544544
545 >>> for spec in kubuntu.specifications(filter=['package']):545 >>> for spec in kubuntu.specifications(None, filter=['package']):
546 ... print spec.name546 ... print spec.name
547 revu547 revu
548548
@@ -550,7 +550,7 @@
550550
551 >>> from lp.blueprints.enums import SpecificationDefinitionStatus551 >>> from lp.blueprints.enums import SpecificationDefinitionStatus
552 >>> login('mark@example.com')552 >>> login('mark@example.com')
553 >>> for spec in kubuntu.specifications():553 >>> for spec in kubuntu.specifications(None):
554 ... # Do this here, otherwise, the change will be flush before554 ... # Do this here, otherwise, the change will be flush before
555 ... # updateLifecycleStatus() acts and an IntegrityError will be555 ... # updateLifecycleStatus() acts and an IntegrityError will be
556 ... # raised.556 ... # raised.
557557
=== modified file 'lib/lp/registry/doc/distroseries.txt'
--- lib/lp/registry/doc/distroseries.txt 2012-09-27 02:53:00 +0000
+++ lib/lp/registry/doc/distroseries.txt 2012-09-28 14:42:25 +0000
@@ -850,14 +850,14 @@
850First, there should be one informational specs for krunch:850First, there should be one informational specs for krunch:
851851
852 >>> filter = [SpecificationFilter.INFORMATIONAL]852 >>> filter = [SpecificationFilter.INFORMATIONAL]
853 >>> krunch.specifications(filter=filter).count()853 >>> krunch.specifications(None, filter=filter).count()
854 1854 1
855855
856856
857There are 2 completed specs for Krunch:857There are 2 completed specs for Krunch:
858858
859 >>> filter = [SpecificationFilter.COMPLETE]859 >>> filter = [SpecificationFilter.COMPLETE]
860 >>> for spec in kubuntu.specifications(filter=filter):860 >>> for spec in kubuntu.specifications(None, filter=filter):
861 ... print spec.name, spec.is_complete861 ... print spec.name, spec.is_complete
862 thinclient-local-devices True862 thinclient-local-devices True
863 usplash-on-hibernation True863 usplash-on-hibernation True
@@ -866,7 +866,7 @@
866And there are 2 incomplete specs:866And there are 2 incomplete specs:
867867
868 >>> filter = [SpecificationFilter.INCOMPLETE]868 >>> filter = [SpecificationFilter.INCOMPLETE]
869 >>> for spec in krunch.specifications(filter=filter):869 >>> for spec in krunch.specifications(None, filter=filter):
870 ... print spec.name, spec.is_complete870 ... print spec.name, spec.is_complete
871 cluster-installation False871 cluster-installation False
872 revu False872 revu False
@@ -875,7 +875,7 @@
875If we ask for all specs, we get them in the order of priority.875If we ask for all specs, we get them in the order of priority.
876876
877 >>> filter = [SpecificationFilter.ALL]877 >>> filter = [SpecificationFilter.ALL]
878 >>> for spec in krunch.specifications(filter=filter):878 >>> for spec in krunch.specifications(None, filter=filter):
879 ... print spec.priority.title, spec.name879 ... print spec.priority.title, spec.name
880 Essential cluster-installation880 Essential cluster-installation
881 High revu881 High revu
@@ -888,7 +888,7 @@
888With a distroseries, we can ask for ACCEPTED, PROPOSED and DECLINED specs:888With a distroseries, we can ask for ACCEPTED, PROPOSED and DECLINED specs:
889889
890 >>> filter=[SpecificationFilter.ACCEPTED]890 >>> filter=[SpecificationFilter.ACCEPTED]
891 >>> for spec in krunch.specifications(filter=filter):891 >>> for spec in krunch.specifications(None, filter=filter):
892 ... print spec.name, spec.goalstatus.title892 ... print spec.name, spec.goalstatus.title
893 cluster-installation Accepted893 cluster-installation Accepted
894 revu Accepted894 revu Accepted
@@ -896,12 +896,12 @@
896 usplash-on-hibernation Accepted896 usplash-on-hibernation Accepted
897897
898 >>> filter=[SpecificationFilter.PROPOSED]898 >>> filter=[SpecificationFilter.PROPOSED]
899 >>> for spec in krunch.specifications(filter=filter):899 >>> for spec in krunch.specifications(None, filter=filter):
900 ... print spec.name, spec.goalstatus.title900 ... print spec.name, spec.goalstatus.title
901 kde-desktopfile-langpacks Proposed901 kde-desktopfile-langpacks Proposed
902902
903 >>> filter=[SpecificationFilter.DECLINED]903 >>> filter=[SpecificationFilter.DECLINED]
904 >>> for spec in krunch.specifications(filter=filter):904 >>> for spec in krunch.specifications(None, filter=filter):
905 ... print spec.name, spec.goalstatus.title905 ... print spec.name, spec.goalstatus.title
906 krunch-desktop-plan Declined906 krunch-desktop-plan Declined
907907
@@ -909,7 +909,7 @@
909And if we ask just for specs, we get BOTH the incomplete and the complete909And if we ask just for specs, we get BOTH the incomplete and the complete
910ones that have been accepted.910ones that have been accepted.
911911
912 >>> for spec in krunch.specifications():912 >>> for spec in krunch.specifications(None):
913 ... print spec.name, spec.is_complete, spec.goalstatus.title913 ... print spec.name, spec.is_complete, spec.goalstatus.title
914 cluster-installation False Accepted914 cluster-installation False Accepted
915 revu False Accepted915 revu False Accepted
@@ -918,7 +918,7 @@
918918
919We can filter for specifications that contain specific text:919We can filter for specifications that contain specific text:
920920
921 >>> for spec in krunch.specifications(filter=['usb']):921 >>> for spec in krunch.specifications(None, filter=['usb']):
922 ... print spec.name922 ... print spec.name
923 thinclient-local-devices923 thinclient-local-devices
924924
925925
=== modified file 'lib/lp/registry/doc/person-account.txt'
--- lib/lp/registry/doc/person-account.txt 2012-09-07 18:06:37 +0000
+++ lib/lp/registry/doc/person-account.txt 2012-09-28 14:42:25 +0000
@@ -70,7 +70,7 @@
70 >>> personset = getUtility(IPersonSet)70 >>> personset = getUtility(IPersonSet)
71 >>> foobar_preferredemail = emailset.getByEmail('foo.bar@canonical.com')71 >>> foobar_preferredemail = emailset.getByEmail('foo.bar@canonical.com')
72 >>> foobar = personset.get(foobar_preferredemail.personID)72 >>> foobar = personset.get(foobar_preferredemail.personID)
73 >>> foobar.specifications().count() > 073 >>> foobar.specifications(None).count() > 0
74 True74 True
7575
76 >>> from lp.blueprints.model.specification import Specification76 >>> from lp.blueprints.model.specification import Specification
7777
=== modified file 'lib/lp/registry/doc/person.txt'
--- lib/lp/registry/doc/person.txt 2012-09-26 07:45:31 +0000
+++ lib/lp/registry/doc/person.txt 2012-09-28 14:42:25 +0000
@@ -1146,7 +1146,7 @@
1146him:1146him:
11471147
1148 >>> from lp.blueprints.enums import SpecificationFilter1148 >>> from lp.blueprints.enums import SpecificationFilter
1149 >>> carlos.specifications(filter=[1149 >>> carlos.specifications(None, filter=[
1150 ... SpecificationFilter.ASSIGNEE,1150 ... SpecificationFilter.ASSIGNEE,
1151 ... SpecificationFilter.COMPLETE]).count()1151 ... SpecificationFilter.COMPLETE]).count()
1152 01152 0
@@ -1154,7 +1154,7 @@
1154Next, Carlos has two incomplete specs *related* to him:1154Next, Carlos has two incomplete specs *related* to him:
11551155
1156 >>> filter = []1156 >>> filter = []
1157 >>> for spec in carlos.specifications(filter=filter):1157 >>> for spec in carlos.specifications(None, filter=filter):
1158 ... print spec.name, spec.is_complete, spec.informational1158 ... print spec.name, spec.is_complete, spec.informational
1159 svg-support False False1159 svg-support False False
1160 extension-manager-upgrades False True1160 extension-manager-upgrades False True
@@ -1177,39 +1177,39 @@
11771177
1178 >>> mark = getUtility(IPersonSet).getByName('mark')1178 >>> mark = getUtility(IPersonSet).getByName('mark')
1179 >>> filter = [SpecificationFilter.APPROVER]1179 >>> filter = [SpecificationFilter.APPROVER]
1180 >>> for spec in mark.specifications(filter=filter):1180 >>> for spec in mark.specifications(None, filter=filter):
1181 ... print spec.name1181 ... print spec.name
1182 extension-manager-upgrades1182 extension-manager-upgrades
11831183
1184But has registered 5 of them:1184But has registered 5 of them:
11851185
1186 >>> filter = [SpecificationFilter.CREATOR]1186 >>> filter = [SpecificationFilter.CREATOR]
1187 >>> print foobar.specifications(filter=filter).count()1187 >>> print foobar.specifications(None, filter=filter).count()
1188 51188 5
11891189
1190Now Celso, on the other hand, has 2 specs related to him:1190Now Celso, on the other hand, has 2 specs related to him:
11911191
1192 >>> cprov = personset.getByName('cprov')1192 >>> cprov = personset.getByName('cprov')
1193 >>> cprov.specifications().count()1193 >>> cprov.specifications(None).count()
1194 21194 2
11951195
1196On one of those, he is the approver:1196On one of those, he is the approver:
11971197
1198 >>> filter = [SpecificationFilter.APPROVER]1198 >>> filter = [SpecificationFilter.APPROVER]
1199 >>> for spec in cprov.specifications(filter=filter):1199 >>> for spec in cprov.specifications(None, filter=filter):
1200 ... print spec.name1200 ... print spec.name
1201 svg-support1201 svg-support
12021202
1203And on another one, he is the drafter1203And on another one, he is the drafter
12041204
1205 >>> filter = [SpecificationFilter.DRAFTER]1205 >>> filter = [SpecificationFilter.DRAFTER]
1206 >>> for spec in cprov.specifications(filter=filter):1206 >>> for spec in cprov.specifications(None, filter=filter):
1207 ... print spec.name1207 ... print spec.name
1208 e4x1208 e4x
12091209
1210We can filter for specifications that contain specific text:1210We can filter for specifications that contain specific text:
12111211
1212 >>> for spec in cprov.specifications(filter=['svg']):1212 >>> for spec in cprov.specifications(None, filter=['svg']):
1213 ... print spec.name1213 ... print spec.name
1214 svg-support1214 svg-support
12151215
@@ -1225,7 +1225,7 @@
1225 >>> unlink_source_packages(firefox)1225 >>> unlink_source_packages(firefox)
1226 >>> firefox.active = False1226 >>> firefox.active = False
1227 >>> flush_database_updates()1227 >>> flush_database_updates()
1228 >>> cprov.specifications(filter=['svg']).count()1228 >>> cprov.specifications(None, filter=['svg']).count()
1229 01229 0
12301230
1231Reset firefox so we don't mess up later tests.1231Reset firefox so we don't mess up later tests.
12321232
=== modified file 'lib/lp/registry/doc/product.txt'
--- lib/lp/registry/doc/product.txt 2012-09-25 16:30:05 +0000
+++ lib/lp/registry/doc/product.txt 2012-09-28 14:42:25 +0000
@@ -384,7 +384,7 @@
384First, there should be only one informational spec for firefox:384First, there should be only one informational spec for firefox:
385385
386 >>> filter = [SpecificationFilter.INFORMATIONAL]386 >>> filter = [SpecificationFilter.INFORMATIONAL]
387 >>> for spec in firefox.specifications(filter=filter):387 >>> for spec in firefox.specifications(None, filter=filter):
388 ... print spec.name388 ... print spec.name
389 extension-manager-upgrades389 extension-manager-upgrades
390390
@@ -392,19 +392,19 @@
392There are no completed specs for firefox:392There are no completed specs for firefox:
393393
394 >>> filter = [SpecificationFilter.COMPLETE]394 >>> filter = [SpecificationFilter.COMPLETE]
395 >>> for spec in firefox.specifications(filter=filter):395 >>> for spec in firefox.specifications(None, filter=filter):
396 ... print spec.name396 ... print spec.name
397397
398398
399And there are five incomplete specs:399And there are five incomplete specs:
400400
401 >>> filter = [SpecificationFilter.INCOMPLETE]401 >>> filter = [SpecificationFilter.INCOMPLETE]
402 >>> firefox.specifications(filter=filter).count()402 >>> firefox.specifications(None, filter=filter).count()
403 5403 5
404404
405We can filter for specifications that contain specific text:405We can filter for specifications that contain specific text:
406406
407 >>> for spec in firefox.specifications(filter=['new']):407 >>> for spec in firefox.specifications(None, filter=['new']):
408 ... print spec.name408 ... print spec.name
409 canvas409 canvas
410 e4x410 e4x
411411
=== modified file 'lib/lp/registry/doc/productseries.txt'
--- lib/lp/registry/doc/productseries.txt 2012-04-10 14:01:17 +0000
+++ lib/lp/registry/doc/productseries.txt 2012-09-28 14:42:25 +0000
@@ -229,7 +229,7 @@
229If we ask for ALL specs we should see them both.229If we ask for ALL specs we should see them both.
230230
231 >>> filter = [SpecificationFilter.ALL]231 >>> filter = [SpecificationFilter.ALL]
232 >>> for s in onezero.specifications(filter=filter):232 >>> for s in onezero.specifications(None, filter=filter):
233 ... print s.name233 ... print s.name
234 a234 a
235 b235 b
@@ -238,23 +238,23 @@
238specs:238specs:
239239
240 >>> filter=[SpecificationFilter.ACCEPTED]240 >>> filter=[SpecificationFilter.ACCEPTED]
241 >>> for spec in onezero.specifications(filter=filter):241 >>> for spec in onezero.specifications(None, filter=filter):
242 ... print spec.name, spec.goalstatus.title242 ... print spec.name, spec.goalstatus.title
243 a Accepted243 a Accepted
244244
245 >>> filter=[SpecificationFilter.PROPOSED]245 >>> filter=[SpecificationFilter.PROPOSED]
246 >>> onezero.specifications(filter=filter).count()246 >>> onezero.specifications(None, filter=filter).count()
247 0247 0
248248
249 >>> filter=[SpecificationFilter.DECLINED]249 >>> filter=[SpecificationFilter.DECLINED]
250 >>> onezero.specifications(filter=filter).count()250 >>> onezero.specifications(None, filter=filter).count()
251 1251 1
252252
253We should see one informational spec if we ask just for that, the253We should see one informational spec if we ask just for that, the
254accepted one.254accepted one.
255255
256 >>> filter = [SpecificationFilter.INFORMATIONAL]256 >>> filter = [SpecificationFilter.INFORMATIONAL]
257 >>> for s in onezero.specifications(filter=filter):257 >>> for s in onezero.specifications(None, filter=filter):
258 ... print s.name258 ... print s.name
259 a259 a
260260
@@ -262,21 +262,21 @@
262262
263 >>> filter = [263 >>> filter = [
264 ... SpecificationFilter.INFORMATIONAL, SpecificationFilter.DECLINED]264 ... SpecificationFilter.INFORMATIONAL, SpecificationFilter.DECLINED]
265 >>> for s in onezero.specifications(filter=filter):265 >>> for s in onezero.specifications(None, filter=filter):
266 ... print s.name266 ... print s.name
267 b267 b
268268
269There are is one completed, accepted spec for 1.0:269There are is one completed, accepted spec for 1.0:
270270
271 >>> filter = [SpecificationFilter.COMPLETE]271 >>> filter = [SpecificationFilter.COMPLETE]
272 >>> for spec in onezero.specifications(filter=filter):272 >>> for spec in onezero.specifications(None, filter=filter):
273 ... print spec.name, spec.is_complete, spec.goalstatus.title273 ... print spec.name, spec.is_complete, spec.goalstatus.title
274 a True Accepted274 a True Accepted
275275
276There is one completed, declined spec:276There is one completed, declined spec:
277277
278 >>> filter = [SpecificationFilter.COMPLETE, SpecificationFilter.DECLINED]278 >>> filter = [SpecificationFilter.COMPLETE, SpecificationFilter.DECLINED]
279 >>> for spec in onezero.specifications(filter=filter):279 >>> for spec in onezero.specifications(None, filter=filter):
280 ... print spec.name, spec.is_complete, spec.goalstatus.title280 ... print spec.name, spec.is_complete, spec.goalstatus.title
281 b True Declined281 b True Declined
282282
@@ -291,7 +291,7 @@
291And if we ask just for specs, we get BOTH the incomplete and the291And if we ask just for specs, we get BOTH the incomplete and the
292complete ones that have been accepted.292complete ones that have been accepted.
293293
294 >>> for spec in onezero.specifications():294 >>> for spec in onezero.specifications(None):
295 ... print spec.name, spec.is_complete, spec.goalstatus.title295 ... print spec.name, spec.is_complete, spec.goalstatus.title
296 a True Accepted296 a True Accepted
297 b False Accepted297 b False Accepted
@@ -299,7 +299,7 @@
299We can search for text in specifications (in this case there are no299We can search for text in specifications (in this case there are no
300matches):300matches):
301301
302 >>> print len(list(onezero.specifications(filter=['new'])))302 >>> print len(list(onezero.specifications(None, filter=['new'])))
303 0303 0
304304
305305
306306
=== modified file 'lib/lp/registry/doc/projectgroup.txt'
--- lib/lp/registry/doc/projectgroup.txt 2012-09-28 14:42:25 +0000
+++ lib/lp/registry/doc/projectgroup.txt 2012-09-28 14:42:25 +0000
@@ -150,7 +150,7 @@
150First, there should be only one informational spec for mozilla:150First, there should be only one informational spec for mozilla:
151151
152 >>> filter = [SpecificationFilter.INFORMATIONAL]152 >>> filter = [SpecificationFilter.INFORMATIONAL]
153 >>> for spec in mozilla.specifications(filter=filter):153 >>> for spec in mozilla.specifications(None, filter=filter):
154 ... print spec.name154 ... print spec.name
155 extension-manager-upgrades155 extension-manager-upgrades
156156
@@ -158,19 +158,19 @@
158There are no completed specs for mozilla:158There are no completed specs for mozilla:
159159
160 >>> filter = [SpecificationFilter.COMPLETE]160 >>> filter = [SpecificationFilter.COMPLETE]
161 >>> for spec in mozilla.specifications(filter=filter):161 >>> for spec in mozilla.specifications(None, filter=filter):
162 ... print spec.name162 ... print spec.name
163163
164164
165And there are five incomplete specs:165And there are five incomplete specs:
166166
167 >>> filter = [SpecificationFilter.INCOMPLETE]167 >>> filter = [SpecificationFilter.INCOMPLETE]
168 >>> mozilla.specifications(filter=filter).count()168 >>> mozilla.specifications(None, filter=filter).count()
169 5169 5
170170
171We can filter for specifications that contain specific text:171We can filter for specifications that contain specific text:
172172
173 >>> for spec in mozilla.specifications(filter=['install']):173 >>> for spec in mozilla.specifications(None, filter=['install']):
174 ... print spec.name174 ... print spec.name
175 extension-manager-upgrades175 extension-manager-upgrades
176176
@@ -178,7 +178,7 @@
178Inactive products are excluded from the listings.178Inactive products are excluded from the listings.
179179
180 >>> filter = [SpecificationFilter.INCOMPLETE]180 >>> filter = [SpecificationFilter.INCOMPLETE]
181 >>> mozilla.specifications(filter=filter).count()181 >>> mozilla.specifications(None, filter=filter).count()
182 5182 5
183183
184 >>> from lp.registry.interfaces.product import IProductSet184 >>> from lp.registry.interfaces.product import IProductSet
@@ -190,7 +190,7 @@
190 >>> firefox.active = False190 >>> firefox.active = False
191 >>> flush_database_updates()191 >>> flush_database_updates()
192 >>> filter = [SpecificationFilter.INCOMPLETE]192 >>> filter = [SpecificationFilter.INCOMPLETE]
193 >>> mozilla.specifications(filter=filter).count()193 >>> mozilla.specifications(None, filter=filter).count()
194 0194 0
195195
196Reset firefox so we don't mess up later tests.196Reset firefox so we don't mess up later tests.
@@ -248,7 +248,8 @@
248mozilla_1_0_series._all_specifications.248mozilla_1_0_series._all_specifications.
249249
250 >>> filter = [SpecificationFilter.INFORMATIONAL]250 >>> filter = [SpecificationFilter.INFORMATIONAL]
251 >>> extension_manager_upgrades = mozilla.specifications(filter=filter)[0]251 >>> extension_manager_upgrades = mozilla.specifications(
252 ... None, filter=filter)[0]
252 >>> series_1_0 = firefox.getSeries('1.0')253 >>> series_1_0 = firefox.getSeries('1.0')
253 >>> extension_manager_upgrades.proposeGoal(series_1_0, no_priv)254 >>> extension_manager_upgrades.proposeGoal(series_1_0, no_priv)
254 >>> for spec in mozilla_series_1_0._all_specifications:255 >>> for spec in mozilla_series_1_0._all_specifications:
@@ -264,7 +265,7 @@
264Filtered lists of project series related specifications are generated265Filtered lists of project series related specifications are generated
265the same way as for project related specifications.266the same way as for project related specifications.
266267
267 >>> for spec in mozilla_series_1_0.specifications(filter=filter):268 >>> for spec in mozilla_series_1_0.specifications(None, filter=filter):
268 ... print spec.name269 ... print spec.name
269 extension-manager-upgrades270 extension-manager-upgrades
270271
@@ -277,7 +278,7 @@
277project itself.278project itself.
278279
279 >>> filter = [SpecificationFilter.INCOMPLETE]280 >>> filter = [SpecificationFilter.INCOMPLETE]
280 >>> for spec in mozilla_series_1_0.specifications(filter=filter):281 >>> for spec in mozilla_series_1_0.specifications(None, filter=filter):
281 ... print spec.name282 ... print spec.name
282 svg-support283 svg-support
283 canvas284 canvas
@@ -287,21 +288,22 @@
287288
288 Searching for text is also possible.289 Searching for text is also possible.
289290
290 >>> for spec in mozilla_series_1_0.specifications(filter=['install']):291 >>> for spec in mozilla_series_1_0.specifications(
292 ... None, filter=['install']):
291 ... print spec.name293 ... print spec.name
292 extension-manager-upgrades294 extension-manager-upgrades
293295
294Inactive products are excluded from the series listings.296Inactive products are excluded from the series listings.
295297
296 >>> filter = [SpecificationFilter.INCOMPLETE]298 >>> filter = [SpecificationFilter.INCOMPLETE]
297 >>> specs = mozilla_series_1_0.specifications(filter=filter)299 >>> specs = mozilla_series_1_0.specifications(None, filter=filter)
298 >>> print specs.count()300 >>> print specs.count()
299 5301 5
300302
301 >>> firefox = getUtility(IProductSet).getByName('firefox')303 >>> firefox = getUtility(IProductSet).getByName('firefox')
302 >>> firefox.active = False304 >>> firefox.active = False
303 >>> filter = [SpecificationFilter.INCOMPLETE]305 >>> filter = [SpecificationFilter.INCOMPLETE]
304 >>> mozilla_series_1_0.specifications(filter=filter).count()306 >>> mozilla_series_1_0.specifications(None, filter=filter).count()
305 0307 0
306308
307Reset firefox so we don't mess up later tests.309Reset firefox so we don't mess up later tests.
308310
=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py 2012-09-28 14:42:25 +0000
+++ lib/lp/registry/model/distribution.py 2012-09-28 14:42:25 +0000
@@ -878,7 +878,7 @@
878 return getUtility(IDistributionSet).getCurrentSourceReleases(878 return getUtility(IDistributionSet).getCurrentSourceReleases(
879 {self: source_package_names})879 {self: source_package_names})
880880
881 def specifications(self, sort=None, quantity=None, filter=None,881 def specifications(self, user, sort=None, quantity=None, filter=None,
882 prejoin_people=True):882 prejoin_people=True):
883 """See `IHasSpecifications`.883 """See `IHasSpecifications`.
884884
885885
=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py 2012-09-28 14:42:25 +0000
+++ lib/lp/registry/model/distroseries.py 2012-09-28 14:42:25 +0000
@@ -777,7 +777,7 @@
777 """See `IHasBugs`."""777 """See `IHasBugs`."""
778 return self.distribution.official_bug_tags778 return self.distribution.official_bug_tags
779779
780 def specifications(self, sort=None, quantity=None, filter=None,780 def specifications(self, user, sort=None, quantity=None, filter=None,
781 prejoin_people=True):781 prejoin_people=True):
782 """See IHasSpecifications.782 """See IHasSpecifications.
783783
784784
=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py 2012-09-28 14:42:25 +0000
+++ lib/lp/registry/model/person.py 2012-09-28 14:42:25 +0000
@@ -821,7 +821,7 @@
821 """See `IPerson`."""821 """See `IPerson`."""
822 return "%s (%s)" % (self.displayname, self.name)822 return "%s (%s)" % (self.displayname, self.name)
823823
824 def specifications(self, sort=None, quantity=None, filter=None,824 def specifications(self, user, sort=None, quantity=None, filter=None,
825 prejoin_people=True):825 prejoin_people=True):
826 """See `IHasSpecifications`."""826 """See `IHasSpecifications`."""
827827
828828
=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py 2012-09-28 14:42:25 +0000
+++ lib/lp/registry/model/product.py 2012-09-28 14:42:25 +0000
@@ -1303,7 +1303,7 @@
1303 # automatically shared.1303 # automatically shared.
1304 return True1304 return True
13051305
1306 def specifications(self, sort=None, quantity=None, filter=None,1306 def specifications(self, user, sort=None, quantity=None, filter=None,
1307 prejoin_people=True):1307 prejoin_people=True):
1308 """See `IHasSpecifications`."""1308 """See `IHasSpecifications`."""
13091309
13101310
=== modified file 'lib/lp/registry/model/productseries.py'
--- lib/lp/registry/model/productseries.py 2012-09-28 14:42:25 +0000
+++ lib/lp/registry/model/productseries.py 2012-09-28 14:42:25 +0000
@@ -301,7 +301,7 @@
301 """See `IProductSeries`."""301 """See `IProductSeries`."""
302 return self == self.product.development_focus302 return self == self.product.development_focus
303303
304 def specifications(self, sort=None, quantity=None, filter=None,304 def specifications(self, user, sort=None, quantity=None, filter=None,
305 prejoin_people=True):305 prejoin_people=True):
306 """See IHasSpecifications.306 """See IHasSpecifications.
307307
308308
=== modified file 'lib/lp/registry/model/projectgroup.py'
--- lib/lp/registry/model/projectgroup.py 2012-09-28 14:42:25 +0000
+++ lib/lp/registry/model/projectgroup.py 2012-09-28 14:42:25 +0000
@@ -231,7 +231,7 @@
231 """ % sqlvalues(self, SprintSpecificationStatus.ACCEPTED)231 """ % sqlvalues(self, SprintSpecificationStatus.ACCEPTED)
232 return query, ['Product', 'Specification', 'SprintSpecification']232 return query, ['Product', 'Specification', 'SprintSpecification']
233233
234 def specifications(self, sort=None, quantity=None, filter=None,234 def specifications(self, user, sort=None, quantity=None, filter=None,
235 series=None, prejoin_people=True):235 series=None, prejoin_people=True):
236 """See `IHasSpecifications`."""236 """See `IHasSpecifications`."""
237237
@@ -631,10 +631,11 @@
631 self.project = project631 self.project = project
632 self.name = name632 self.name = name
633633
634 def specifications(self, sort=None, quantity=None, filter=None,634 def specifications(self, user, sort=None, quantity=None, filter=None,
635 prejoin_people=True):635 prejoin_people=True):
636 return self.project.specifications(636 return self.project.specifications(
637 sort, quantity, filter, self.name, prejoin_people=prejoin_people)637 user, sort, quantity, filter, self.name,
638 prejoin_people=prejoin_people)
638639
639 @property640 @property
640 def title(self):641 def title(self):
641642
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2012-09-28 06:25:44 +0000
+++ lib/lp/testing/factory.py 2012-09-28 14:42:25 +0000
@@ -2091,8 +2091,16 @@
2091 :param product: The product to make the blueprint on. If one is2091 :param product: The product to make the blueprint on. If one is
2092 not specified, an arbitrary product is created.2092 not specified, an arbitrary product is created.
2093 """2093 """
2094 proprietary = (information_type not in PUBLIC_INFORMATION_TYPES and
2095 information_type is not None)
2094 if distribution is None and product is None:2096 if distribution is None and product is None:
2095 product = self.makeProduct()2097 if proprietary:
2098 specification_sharing_policy = (
2099 SpecificationSharingPolicy.EMBARGOED_OR_PROPRIETARY)
2100 else:
2101 specification_sharing_policy = None
2102 product = self.makeProduct(
2103 specification_sharing_policy=specification_sharing_policy)
2096 if name is None:2104 if name is None:
2097 name = self.getUniqueString('name')2105 name = self.getUniqueString('name')
2098 if summary is None:2106 if summary is None:
@@ -2125,7 +2133,7 @@
2125 priority=priority)2133 priority=priority)
2126 naked_spec = removeSecurityProxy(spec)2134 naked_spec = removeSecurityProxy(spec)
2127 if information_type is not None:2135 if information_type is not None:
2128 if information_type not in PUBLIC_INFORMATION_TYPES:2136 if proprietary:
2129 naked_spec.target._ensurePolicies([information_type])2137 naked_spec.target._ensurePolicies([information_type])
2130 naked_spec.transitionToInformationType(2138 naked_spec.transitionToInformationType(
2131 information_type, spec.target.owner)2139 information_type, spec.target.owner)
@@ -4340,9 +4348,9 @@
4340 return link4348 return link
43414349
4342 def makeAccessArtifactGrant(self, artifact=None, grantee=None,4350 def makeAccessArtifactGrant(self, artifact=None, grantee=None,
4343 grantor=None):4351 grantor=None, concrete_artifact=None):
4344 if artifact is None:4352 if artifact is None:
4345 artifact = self.makeAccessArtifact()4353 artifact = self.makeAccessArtifact(concrete_artifact)
4346 if grantee is None:4354 if grantee is None:
4347 grantee = self.makePerson()4355 grantee = self.makePerson()
4348 if grantor is None:4356 if grantor is None: