Merge lp:~abentley/launchpad/hide-sprint-blueprints into lp:launchpad
- hide-sprint-blueprints
- Merge into devel
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 | ||||
Related bugs: |
|
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.
== Pre-implementation notes ==
None
== LOC Rationale ==
Part of private projects
== Implementation details ==
IHasSpecificati
Sprint.
All callers are updated to supply a user. View methods supply self.user, and model methods accept an additional parameter. _all_specifications and _valid_
== 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/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
./lib/lp/
408: redefinition of function 'date_next_
^^^ This is an acceptable case of re-definition. It's creating a setter.
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
1 | === modified file 'lib/lp/app/browser/root.py' |
2 | --- lib/lp/app/browser/root.py 2012-09-28 14:42:25 +0000 |
3 | +++ lib/lp/app/browser/root.py 2012-09-28 14:42:25 +0000 |
4 | @@ -130,7 +130,7 @@ |
5 | @property |
6 | def blueprint_count(self): |
7 | """The total blueprint count in all of Launchpad.""" |
8 | - return getUtility(ISpecificationSet).specificationCount() |
9 | + return getUtility(ISpecificationSet).specificationCount(self.user) |
10 | |
11 | @property |
12 | def answer_count(self): |
13 | |
14 | === modified file 'lib/lp/blueprints/browser/specification.py' |
15 | --- lib/lp/blueprints/browser/specification.py 2012-09-28 14:42:25 +0000 |
16 | +++ lib/lp/blueprints/browser/specification.py 2012-09-28 14:42:25 +0000 |
17 | @@ -1036,7 +1036,7 @@ |
18 | """Override the setup to define own fields.""" |
19 | if self.context.target is None: |
20 | raise AssertionError("No target found for this spec.") |
21 | - specs = sorted(self.context.target.specifications(), |
22 | + specs = sorted(self.context.target.specifications(self.user), |
23 | key=attrgetter('name')) |
24 | terms = [SimpleTerm(spec, spec.name, spec.title) |
25 | for spec in specs if spec != self.context] |
26 | @@ -1530,17 +1530,17 @@ |
27 | @property |
28 | def latest_specifications(self): |
29 | return self.context.specifications( |
30 | - sort=SpecificationSort.DATE, quantity=5) |
31 | + self.user, sort=SpecificationSort.DATE, quantity=5) |
32 | |
33 | @property |
34 | def latest_completed_specifications(self): |
35 | return self.context.specifications( |
36 | - sort=SpecificationSort.DATE, quantity=5, |
37 | + self.user, sort=SpecificationSort.DATE, quantity=5, |
38 | filter=[SpecificationFilter.COMPLETE]) |
39 | |
40 | @property |
41 | def specification_count(self): |
42 | - return self.context.specificationCount() |
43 | + return self.context.specificationCount(self.user) |
44 | |
45 | @safe_action |
46 | @action('Find blueprints', name="search") |
47 | |
48 | === modified file 'lib/lp/blueprints/browser/specificationtarget.py' |
49 | --- lib/lp/blueprints/browser/specificationtarget.py 2012-09-28 14:42:25 +0000 |
50 | +++ lib/lp/blueprints/browser/specificationtarget.py 2012-09-28 14:42:25 +0000 |
51 | @@ -259,11 +259,11 @@ |
52 | |
53 | @cachedproperty |
54 | def has_any_specifications(self): |
55 | - return self.context._all_specifications.count() != 0 |
56 | + return not self.context._all_specifications.is_empty() |
57 | |
58 | @cachedproperty |
59 | def all_specifications(self): |
60 | - return shortlist(self.context.all_specifications) |
61 | + return shortlist(self.context.all_specifications(self.user)) |
62 | |
63 | @cachedproperty |
64 | def searchrequested(self): |
65 | @@ -348,7 +348,7 @@ |
66 | and not check_permission('launchpad.View', self.context)): |
67 | return [] |
68 | filter = self.spec_filter |
69 | - return self.context.specifications(filter=filter) |
70 | + return self.context.specifications(self.user, filter=filter) |
71 | |
72 | @cachedproperty |
73 | def specs_batched(self): |
74 | @@ -364,7 +364,7 @@ |
75 | def documentation(self): |
76 | filter = [SpecificationFilter.COMPLETE, |
77 | SpecificationFilter.INFORMATIONAL] |
78 | - return shortlist(self.context.specifications(filter=filter)) |
79 | + return shortlist(self.context.specifications(self.user, filter=filter)) |
80 | |
81 | @cachedproperty |
82 | def categories(self): |
83 | @@ -405,8 +405,9 @@ |
84 | Only ACCEPTED specifications are returned. This list is used by the |
85 | +portlet-latestspecs view. |
86 | """ |
87 | - return self.context.specifications(sort=SpecificationSort.DATE, |
88 | - quantity=quantity, prejoin_people=False) |
89 | + return self.context.specifications(self.user, |
90 | + sort=SpecificationSort.DATE, quantity=quantity, |
91 | + prejoin_people=False) |
92 | |
93 | |
94 | class SpecificationAssignmentsView(HasSpecificationsView): |
95 | |
96 | === modified file 'lib/lp/blueprints/browser/sprint.py' |
97 | --- lib/lp/blueprints/browser/sprint.py 2012-01-01 02:58:52 +0000 |
98 | +++ lib/lp/blueprints/browser/sprint.py 2012-09-28 14:42:25 +0000 |
99 | @@ -216,7 +216,7 @@ |
100 | @cachedproperty |
101 | def latest_approved(self): |
102 | filter = [SpecificationFilter.ACCEPTED] |
103 | - return self.context.specifications(filter=filter, |
104 | + return self.context.specifications(self.user, filter=filter, |
105 | quantity=self.latest_specs_limit, |
106 | sort=SpecificationSort.DATE) |
107 | |
108 | @@ -464,7 +464,7 @@ |
109 | |
110 | model_specs = [] |
111 | for spec in self.context.specifications( |
112 | - filter=[SpecificationFilter.ACCEPTED]): |
113 | + self.user, filter=[SpecificationFilter.ACCEPTED]): |
114 | |
115 | # skip sprints with no priority or less than low: |
116 | if spec.priority < SpecificationPriority.UNDEFINED: |
117 | |
118 | === modified file 'lib/lp/blueprints/browser/tests/test_sprint.py' |
119 | --- lib/lp/blueprints/browser/tests/test_sprint.py 2012-09-28 14:42:25 +0000 |
120 | +++ lib/lp/blueprints/browser/tests/test_sprint.py 2012-09-28 14:42:25 +0000 |
121 | @@ -8,6 +8,7 @@ |
122 | from storm.locals import Store |
123 | from testtools.matchers import Equals |
124 | |
125 | +from lp.app.enums import InformationType |
126 | from lp.testing import BrowserTestCase |
127 | from lp.testing.layers import DatabaseFunctionalLayer |
128 | from lp.testing.matchers import BrowsesWithQueryLimit, HasQueryCount |
129 | @@ -40,3 +41,15 @@ |
130 | with QueryCollector() as recorder: |
131 | self.getViewBrowser(sprint) |
132 | self.assertThat(recorder, HasQueryCount(Equals(30))) |
133 | + |
134 | + def test_proprietary_blueprint_listing_query_count(self): |
135 | + """Set a maximum number of queries for sprint blueprint lists.""" |
136 | + sprint = self.factory.makeSprint() |
137 | + for count in range(10): |
138 | + blueprint = self.factory.makeSpecification( |
139 | + information_type=InformationType.PROPRIETARY) |
140 | + link = blueprint.linkSprint(sprint, blueprint.owner) |
141 | + link.acceptBy(sprint.owner) |
142 | + with QueryCollector() as recorder: |
143 | + self.getViewBrowser(sprint) |
144 | + self.assertThat(recorder, HasQueryCount(Equals(22))) |
145 | |
146 | === modified file 'lib/lp/blueprints/doc/specification.txt' |
147 | --- lib/lp/blueprints/doc/specification.txt 2012-09-28 14:42:25 +0000 |
148 | +++ lib/lp/blueprints/doc/specification.txt 2012-09-28 14:42:25 +0000 |
149 | @@ -222,7 +222,7 @@ |
150 | We can filter for specifications that contain specific text, across all |
151 | specifications: |
152 | |
153 | - >>> for spec in specset.specifications(filter=['install']): |
154 | + >>> for spec in specset.specifications(None, filter=['install']): |
155 | ... print spec.name, spec.target.name |
156 | cluster-installation kubuntu |
157 | extension-manager-upgrades firefox |
158 | @@ -239,7 +239,7 @@ |
159 | >>> unlink_source_packages(upstream_firefox) |
160 | >>> upstream_firefox.active = False |
161 | >>> flush_database_updates() |
162 | - >>> for spec in specset.specifications(filter=['install']): |
163 | + >>> for spec in specset.specifications(None, filter=['install']): |
164 | ... print spec.name, spec.target.name |
165 | cluster-installation kubuntu |
166 | media-integrity-check ubuntu |
167 | |
168 | === modified file 'lib/lp/blueprints/doc/sprint.txt' |
169 | --- lib/lp/blueprints/doc/sprint.txt 2011-12-30 06:14:56 +0000 |
170 | +++ lib/lp/blueprints/doc/sprint.txt 2012-09-28 14:42:25 +0000 |
171 | @@ -118,19 +118,19 @@ |
172 | First, there should be no informational specs for ubz: |
173 | |
174 | >>> filter = [SpecificationFilter.INFORMATIONAL] |
175 | - >>> ubz.specifications(filter=filter).count() |
176 | + >>> ubz.specifications(None, filter=filter).count() |
177 | 1 |
178 | |
179 | There are 0 completed specs for UBZ: |
180 | |
181 | >>> filter = [SpecificationFilter.COMPLETE] |
182 | - >>> ubz.specifications(filter=filter).count() |
183 | + >>> ubz.specifications(None, filter=filter).count() |
184 | 0 |
185 | |
186 | And there are three incomplete specs: |
187 | |
188 | >>> filter = [SpecificationFilter.INCOMPLETE] |
189 | - >>> for spec in ubz.specifications(filter=filter): |
190 | + >>> for spec in ubz.specifications(None, filter=filter): |
191 | ... print spec.name, spec.is_complete |
192 | svg-support False |
193 | extension-manager-upgrades False |
194 | @@ -139,7 +139,7 @@ |
195 | If we ask for all specs, we get them in the order of priority. |
196 | |
197 | >>> filter = [SpecificationFilter.ALL] |
198 | - >>> for spec in ubz.specifications(filter=filter): |
199 | + >>> for spec in ubz.specifications(None, filter=filter): |
200 | ... print spec.priority.title, spec.name |
201 | High svg-support |
202 | Medium extension-manager-upgrades |
203 | @@ -147,7 +147,7 @@ |
204 | |
205 | And if we ask just for specs, we get them all |
206 | |
207 | - >>> for spec in ubz.specifications(): |
208 | + >>> for spec in ubz.specifications(None): |
209 | ... print spec.name, spec.is_complete |
210 | svg-support False |
211 | extension-manager-upgrades False |
212 | @@ -167,7 +167,7 @@ |
213 | >>> unlink_source_packages(firefox) |
214 | >>> firefox.active = False |
215 | >>> flush_database_updates() |
216 | - >>> ubz.specifications().count() |
217 | + >>> ubz.specifications(None).count() |
218 | 0 |
219 | |
220 | Reset firefox so we don't mess up later tests. |
221 | |
222 | === modified file 'lib/lp/blueprints/interfaces/specification.py' |
223 | --- lib/lp/blueprints/interfaces/specification.py 2012-09-28 14:42:25 +0000 |
224 | +++ lib/lp/blueprints/interfaces/specification.py 2012-09-28 14:42:25 +0000 |
225 | @@ -674,7 +674,7 @@ |
226 | |
227 | coming_sprints = Attribute("The next 5 sprints in the system.") |
228 | |
229 | - def specificationCount(): |
230 | + def specificationCount(user): |
231 | """The total number of blueprints in Launchpad""" |
232 | |
233 | def getStatusCountsForProductSeries(product_series): |
234 | |
235 | === modified file 'lib/lp/blueprints/interfaces/specificationtarget.py' |
236 | --- lib/lp/blueprints/interfaces/specificationtarget.py 2012-09-28 14:42:25 +0000 |
237 | +++ lib/lp/blueprints/interfaces/specificationtarget.py 2012-09-28 14:42:25 +0000 |
238 | @@ -60,10 +60,11 @@ |
239 | 'have not been accepted for that goal'))), |
240 | exported_as="valid_specifications", as_of="devel") |
241 | |
242 | - def specifications(quantity=None, sort=None, filter=None, |
243 | + def specifications(user, quantity=None, sort=None, filter=None, |
244 | prejoin_people=True): |
245 | """Specifications for this target. |
246 | |
247 | + The user specifies which user to use for calculation of visibility. |
248 | The sort is a dbschema which indicates the preferred sort order. The |
249 | filter is an indicator of the kinds of specs to be returned, and |
250 | appropriate filters depend on the kind of object this method is on. |
251 | |
252 | === modified file 'lib/lp/blueprints/model/specification.py' |
253 | --- lib/lp/blueprints/model/specification.py 2012-09-28 14:42:25 +0000 |
254 | +++ lib/lp/blueprints/model/specification.py 2012-09-28 14:42:25 +0000 |
255 | @@ -109,6 +109,7 @@ |
256 | cachedproperty, |
257 | get_property_cache, |
258 | ) |
259 | +from lp.services.webapp.interfaces import ILaunchBag |
260 | |
261 | |
262 | def recursive_blocked_query(spec): |
263 | @@ -957,7 +958,7 @@ |
264 | for other classes that have specifications. |
265 | """ |
266 | |
267 | - def specifications(self, sort=None, quantity=None, filter=None, |
268 | + def specifications(self, user, sort=None, quantity=None, filter=None, |
269 | prejoin_people=True): |
270 | """See IHasSpecifications.""" |
271 | # this should be implemented by the actual context class |
272 | @@ -1024,16 +1025,19 @@ |
273 | @property |
274 | def _all_specifications(self): |
275 | """See IHasSpecifications.""" |
276 | - return self.specifications(filter=[SpecificationFilter.ALL]) |
277 | + user = getUtility(ILaunchBag).user |
278 | + return self.specifications(user, filter=[SpecificationFilter.ALL]) |
279 | |
280 | @property |
281 | def _valid_specifications(self): |
282 | """See IHasSpecifications.""" |
283 | - return self.specifications(filter=[SpecificationFilter.VALID]) |
284 | + user = getUtility(ILaunchBag).user |
285 | + return self.specifications(user, filter=[SpecificationFilter.VALID]) |
286 | |
287 | - def specificationCount(self): |
288 | + def specificationCount(self, user): |
289 | """See IHasSpecifications.""" |
290 | - return self.specifications(filter=[SpecificationFilter.ALL]).count() |
291 | + return self.specifications(user, |
292 | + filter=[SpecificationFilter.ALL]).count() |
293 | |
294 | |
295 | class SpecificationSet(HasSpecificationsMixin): |
296 | @@ -1072,7 +1076,7 @@ |
297 | """See ISpecificationSet.""" |
298 | return iter(self.all_specifications) |
299 | |
300 | - def specifications(self, sort=None, quantity=None, filter=None, |
301 | + def specifications(self, user, sort=None, quantity=None, filter=None, |
302 | prejoin_people=True): |
303 | """See IHasSpecifications.""" |
304 | |
305 | |
306 | === modified file 'lib/lp/blueprints/model/sprint.py' |
307 | --- lib/lp/blueprints/model/sprint.py 2012-09-28 14:42:25 +0000 |
308 | +++ lib/lp/blueprints/model/sprint.py 2012-09-28 14:42:25 +0000 |
309 | @@ -41,7 +41,10 @@ |
310 | ISprint, |
311 | ISprintSet, |
312 | ) |
313 | -from lp.blueprints.model.specification import HasSpecificationsMixin |
314 | +from lp.blueprints.model.specification import ( |
315 | + get_specification_privacy_filter, |
316 | + HasSpecificationsMixin, |
317 | + ) |
318 | from lp.blueprints.model.sprintattendance import SprintAttendance |
319 | from lp.blueprints.model.sprintspecification import SprintSpecification |
320 | from lp.registry.interfaces.person import ( |
321 | @@ -111,7 +114,7 @@ |
322 | # Only really used in tests. |
323 | return [a.attendee for a in self.attendances] |
324 | |
325 | - def spec_filter_clause(self, filter=None): |
326 | + def spec_filter_clause(self, user, filter=None): |
327 | """Figure out the appropriate query for specifications on a sprint. |
328 | |
329 | We separate out the query generation from the normal |
330 | @@ -126,6 +129,7 @@ |
331 | Or(Specification.product == None, |
332 | Not(Specification.productID.is_in(Select(Product.id, |
333 | Product.active == False))))] |
334 | + query.append(get_specification_privacy_filter(user)) |
335 | if not filter: |
336 | # filter could be None or [] then we decide the default |
337 | # which for a sprint is to show everything approved |
338 | @@ -169,7 +173,10 @@ |
339 | query.append(fti_search(Specification, constraint)) |
340 | return query |
341 | |
342 | - def specifications(self, sort=None, quantity=None, filter=None, |
343 | + def all_specifications(self, user): |
344 | + return self.specifications(user, filter=[SpecificationFilter.ALL]) |
345 | + |
346 | + def specifications(self, user, sort=None, quantity=None, filter=None, |
347 | prejoin_people=False): |
348 | """See IHasSpecifications.""" |
349 | # prejoin_people is provided only for interface compatibility and |
350 | @@ -177,7 +184,7 @@ |
351 | assert not prejoin_people |
352 | if filter is None: |
353 | filter = set([SpecificationFilter.ACCEPTED]) |
354 | - query = self.spec_filter_clause(filter=filter) |
355 | + query = self.spec_filter_clause(user, filter=filter) |
356 | # import here to avoid circular deps |
357 | from lp.blueprints.model.specification import Specification |
358 | results = Store.of(self).find(Specification, *query) |
359 | @@ -200,7 +207,7 @@ |
360 | |
361 | def specificationLinks(self, filter=None): |
362 | """See `ISprint`.""" |
363 | - query = self.spec_filter_clause(filter=filter) |
364 | + query = self.spec_filter_clause(None, filter=filter) |
365 | result = Store.of(self).find(SprintSpecification, *query) |
366 | return result |
367 | |
368 | @@ -227,7 +234,7 @@ |
369 | # queue |
370 | flush_database_updates() |
371 | |
372 | - return self.specifications( |
373 | + return self.specifications(decider, |
374 | filter=[SpecificationFilter.PROPOSED]).count() |
375 | |
376 | def declineSpecificationLinks(self, idlist, decider): |
377 | @@ -241,7 +248,7 @@ |
378 | # queue |
379 | flush_database_updates() |
380 | |
381 | - return self.specifications( |
382 | + return self.specifications(decider, |
383 | filter=[SpecificationFilter.PROPOSED]).count() |
384 | |
385 | # attendance |
386 | |
387 | === modified file 'lib/lp/blueprints/model/tests/test_sprint.py' |
388 | --- lib/lp/blueprints/model/tests/test_sprint.py 2012-09-28 14:42:25 +0000 |
389 | +++ lib/lp/blueprints/model/tests/test_sprint.py 2012-09-28 14:42:25 +0000 |
390 | @@ -9,8 +9,10 @@ |
391 | import datetime |
392 | |
393 | from pytz import utc |
394 | +from zope.component import getUtility |
395 | from zope.security.proxy import removeSecurityProxy |
396 | |
397 | +from lp.app.enums import InformationType |
398 | from lp.blueprints.enums import ( |
399 | NewSpecificationDefinitionStatus, |
400 | SpecificationDefinitionStatus, |
401 | @@ -18,12 +20,13 @@ |
402 | SpecificationPriority, |
403 | SpecificationSort, |
404 | ) |
405 | +from lp.registry.interfaces.accesspolicy import IAccessPolicySource |
406 | from lp.testing import TestCaseWithFactory |
407 | from lp.testing.layers import DatabaseFunctionalLayer |
408 | |
409 | |
410 | -def list_result(sprint, filter=None): |
411 | - result = sprint.specifications(SpecificationSort.DATE, filter=filter) |
412 | +def list_result(sprint, filter=None, user=None): |
413 | + result = sprint.specifications(user, SpecificationSort.DATE, filter=filter) |
414 | return list(result) |
415 | |
416 | |
417 | @@ -38,11 +41,12 @@ |
418 | def makeSpec(self, sprint=None, date_decided=0, date_created=0, |
419 | proposed=False, declined=False, title=None, |
420 | status=NewSpecificationDefinitionStatus.NEW, |
421 | - name=None, priority=None): |
422 | + name=None, priority=None, information_type=None): |
423 | if sprint is None: |
424 | sprint = self.factory.makeSprint() |
425 | blueprint = self.factory.makeSpecification( |
426 | - title=title, status=status, name=name, priority=priority) |
427 | + title=title, status=status, name=name, priority=priority, |
428 | + information_type=information_type) |
429 | link = blueprint.linkSprint(sprint, blueprint.owner) |
430 | naked_link = removeSecurityProxy(link) |
431 | if declined: |
432 | @@ -61,10 +65,11 @@ |
433 | sprint = self.factory.makeSprint() |
434 | for count in range(10): |
435 | self.makeSpec(sprint) |
436 | - self.assertEqual(10, sprint.specifications().count()) |
437 | - self.assertEqual(10, sprint.specifications(quantity=None).count()) |
438 | - self.assertEqual(8, sprint.specifications(quantity=8).count()) |
439 | - self.assertEqual(10, sprint.specifications(quantity=11).count()) |
440 | + self.assertEqual(10, sprint.specifications(None).count()) |
441 | + result = sprint.specifications(None, quantity=None).count() |
442 | + self.assertEqual(10, result) |
443 | + self.assertEqual(8, sprint.specifications(None, quantity=8).count()) |
444 | + self.assertEqual(10, sprint.specifications(None, quantity=11).count()) |
445 | |
446 | def test_specifications_date_sort_accepted_decided(self): |
447 | # If only accepted proposals are requested, date-sorting uses |
448 | @@ -133,9 +138,9 @@ |
449 | blueprint3 = self.makeSpec( |
450 | sprint, priority=SpecificationPriority.LOW, |
451 | status=SpecificationDefinitionStatus.OBSOLETE) |
452 | - result = sprint.specifications() |
453 | + result = sprint.specifications(None) |
454 | self.assertEqual([blueprint3, blueprint1, blueprint2], list(result)) |
455 | - result = sprint.specifications(sort=SpecificationSort.PRIORITY) |
456 | + result = sprint.specifications(None, sort=SpecificationSort.PRIORITY) |
457 | self.assertEqual([blueprint3, blueprint1, blueprint2], list(result)) |
458 | |
459 | def test_priority_sort_fallback_status(self): |
460 | @@ -148,9 +153,9 @@ |
461 | sprint, status=SpecificationDefinitionStatus.APPROVED, name='c') |
462 | blueprint3 = self.makeSpec( |
463 | sprint, status=SpecificationDefinitionStatus.NEW, name='b') |
464 | - result = sprint.specifications() |
465 | + result = sprint.specifications(None) |
466 | self.assertEqual([blueprint2, blueprint3, blueprint1], list(result)) |
467 | - result = sprint.specifications(sort=SpecificationSort.PRIORITY) |
468 | + result = sprint.specifications(None, sort=SpecificationSort.PRIORITY) |
469 | self.assertEqual([blueprint2, blueprint3, blueprint1], list(result)) |
470 | |
471 | def test_priority_sort_fallback_name(self): |
472 | @@ -159,9 +164,9 @@ |
473 | sprint = blueprint1.sprints[0] |
474 | blueprint2 = self.makeSpec(sprint, name='c') |
475 | blueprint3 = self.makeSpec(sprint, name='a') |
476 | - result = sprint.specifications() |
477 | + result = sprint.specifications(None) |
478 | self.assertEqual([blueprint3, blueprint1, blueprint2], list(result)) |
479 | - result = sprint.specifications(sort=SpecificationSort.PRIORITY) |
480 | + result = sprint.specifications(None, sort=SpecificationSort.PRIORITY) |
481 | self.assertEqual([blueprint3, blueprint1, blueprint2], list(result)) |
482 | |
483 | def test_text_search(self): |
484 | @@ -182,6 +187,34 @@ |
485 | result = list_result(sprint, [SpecificationFilter.DECLINED]) |
486 | self.assertEqual([blueprint2], result) |
487 | |
488 | + def test_proprietary_not_listed(self): |
489 | + # Proprietary blueprints are not listed for random users |
490 | + blueprint1 = self.makeSpec( |
491 | + information_type=InformationType.PROPRIETARY) |
492 | + self.assertEqual([], list_result(blueprint1.sprints[0])) |
493 | + |
494 | + def test_proprietary_listed_for_artifact_grant(self): |
495 | + # Proprietary blueprints are listed for users with an artifact grant. |
496 | + blueprint1 = self.makeSpec( |
497 | + information_type=InformationType.PROPRIETARY) |
498 | + grant = self.factory.makeAccessArtifactGrant( |
499 | + concrete_artifact=blueprint1) |
500 | + self.assertEqual( |
501 | + [blueprint1], |
502 | + list_result(blueprint1.sprints[0], user=grant.grantee)) |
503 | + |
504 | + def test_proprietary_listed_for_policy_grant(self): |
505 | + # Proprietary blueprints are listed for users with a policy grant. |
506 | + blueprint1 = self.makeSpec( |
507 | + information_type=InformationType.PROPRIETARY) |
508 | + policy_source = getUtility(IAccessPolicySource) |
509 | + (policy,) = policy_source.find( |
510 | + [(blueprint1.product, InformationType.PROPRIETARY)]) |
511 | + grant = self.factory.makeAccessPolicyGrant(policy) |
512 | + self.assertEqual( |
513 | + [blueprint1], |
514 | + list_result(blueprint1.sprints[0], user=grant.grantee)) |
515 | + |
516 | |
517 | class TestSprintAttendancesSort(TestCaseWithFactory): |
518 | |
519 | |
520 | === modified file 'lib/lp/blueprints/templates/person-specworkload.pt' |
521 | --- lib/lp/blueprints/templates/person-specworkload.pt 2011-12-08 22:41:00 +0000 |
522 | +++ lib/lp/blueprints/templates/person-specworkload.pt 2012-09-28 14:42:25 +0000 |
523 | @@ -44,7 +44,7 @@ |
524 | |
525 | <tal:participants repeat="member members_batch"> |
526 | |
527 | - <tal:specs define="specifications member/specifications"> |
528 | + <tal:specs define="specifications member/@@+specworkload/specifications"> |
529 | |
530 | <div style="margin-bottom: 1em;" tal:condition="specifications"> |
531 | <p> |
532 | |
533 | === modified file 'lib/lp/blueprints/vocabularies/specification.py' |
534 | --- lib/lp/blueprints/vocabularies/specification.py 2012-01-01 02:58:52 +0000 |
535 | +++ lib/lp/blueprints/vocabularies/specification.py 2012-09-28 14:42:25 +0000 |
536 | @@ -39,7 +39,8 @@ |
537 | |
538 | if target is not None: |
539 | for spec in sorted( |
540 | - target.specifications(), key=attrgetter('title')): |
541 | + target.specifications(launchbag.user), |
542 | + key=attrgetter('title')): |
543 | # we will not show the current specification in the |
544 | # launchbag |
545 | if spec == launchbag.specification: |
546 | |
547 | === modified file 'lib/lp/registry/browser/person.py' |
548 | --- lib/lp/registry/browser/person.py 2012-09-11 13:25:48 +0000 |
549 | +++ lib/lp/registry/browser/person.py 2012-09-28 14:42:25 +0000 |
550 | @@ -1303,6 +1303,9 @@ |
551 | batch_nav = BatchNavigator(members, self.request, size=20) |
552 | return batch_nav |
553 | |
554 | + def specifications(self): |
555 | + return self.context.specifications(self.user) |
556 | + |
557 | |
558 | class PersonSpecWorkloadTableView(LaunchpadView): |
559 | """View to render the specification workload table for a person. |
560 | @@ -1331,7 +1334,7 @@ |
561 | approver, the assignee or the drafter. |
562 | """ |
563 | return [PersonSpecWorkloadTableView.PersonSpec(spec, self.context) |
564 | - for spec in self.context.specifications()] |
565 | + for spec in self.context.specifications(self.user)] |
566 | |
567 | |
568 | class PersonVouchersView(LaunchpadFormView): |
569 | @@ -3573,7 +3576,7 @@ |
570 | else: |
571 | project['bug_count'] = pillar.searchTasks( |
572 | BugTaskSet().open_bugtask_search).count() |
573 | - project['spec_count'] = pillar.specifications().count() |
574 | + project['spec_count'] = pillar.specifications(user).count() |
575 | project['question_count'] = pillar.searchQuestions().count() |
576 | projects.append(project) |
577 | return projects |
578 | |
579 | === modified file 'lib/lp/registry/doc/distribution.txt' |
580 | --- lib/lp/registry/doc/distribution.txt 2012-09-28 14:42:25 +0000 |
581 | +++ lib/lp/registry/doc/distribution.txt 2012-09-28 14:42:25 +0000 |
582 | @@ -490,18 +490,18 @@ |
583 | complete so it will not show up unless we explicitly ask for complete specs: |
584 | |
585 | >>> filter = [SpecificationFilter.INFORMATIONAL] |
586 | - >>> kubuntu.specifications(filter=filter).count() |
587 | + >>> kubuntu.specifications(None, filter=filter).count() |
588 | 0 |
589 | >>> filter = [SpecificationFilter.INFORMATIONAL, |
590 | ... SpecificationFilter.COMPLETE] |
591 | - >>> kubuntu.specifications(filter=filter).count() |
592 | + >>> kubuntu.specifications(None, filter=filter).count() |
593 | 1 |
594 | |
595 | |
596 | There are 2 completed specs for Kubuntu: |
597 | |
598 | >>> filter = [SpecificationFilter.COMPLETE] |
599 | - >>> for spec in kubuntu.specifications(filter=filter): |
600 | + >>> for spec in kubuntu.specifications(None, filter=filter): |
601 | ... print spec.name, spec.is_complete |
602 | thinclient-local-devices True |
603 | usplash-on-hibernation True |
604 | @@ -510,7 +510,7 @@ |
605 | And there are four incomplete specs: |
606 | |
607 | >>> filter = [SpecificationFilter.INCOMPLETE] |
608 | - >>> for spec in kubuntu.specifications(filter=filter): |
609 | + >>> for spec in kubuntu.specifications(None, filter=filter): |
610 | ... print spec.name, spec.is_complete |
611 | cluster-installation False |
612 | revu False |
613 | @@ -521,7 +521,7 @@ |
614 | If we ask for all specs, we get them in the order of priority. |
615 | |
616 | >>> filter = [SpecificationFilter.ALL] |
617 | - >>> for spec in kubuntu.specifications(filter=filter): |
618 | + >>> for spec in kubuntu.specifications(None, filter=filter): |
619 | ... print spec.priority.title, spec.name |
620 | Essential cluster-installation |
621 | High revu |
622 | @@ -533,7 +533,7 @@ |
623 | |
624 | And if we ask just for specs, we get the incomplete ones. |
625 | |
626 | - >>> for spec in kubuntu.specifications(): |
627 | + >>> for spec in kubuntu.specifications(None): |
628 | ... print spec.name, spec.is_complete |
629 | cluster-installation False |
630 | revu False |
631 | @@ -542,7 +542,7 @@ |
632 | |
633 | We can filter for specifications that contain specific text: |
634 | |
635 | - >>> for spec in kubuntu.specifications(filter=['package']): |
636 | + >>> for spec in kubuntu.specifications(None, filter=['package']): |
637 | ... print spec.name |
638 | revu |
639 | |
640 | @@ -550,7 +550,7 @@ |
641 | |
642 | >>> from lp.blueprints.enums import SpecificationDefinitionStatus |
643 | >>> login('mark@example.com') |
644 | - >>> for spec in kubuntu.specifications(): |
645 | + >>> for spec in kubuntu.specifications(None): |
646 | ... # Do this here, otherwise, the change will be flush before |
647 | ... # updateLifecycleStatus() acts and an IntegrityError will be |
648 | ... # raised. |
649 | |
650 | === modified file 'lib/lp/registry/doc/distroseries.txt' |
651 | --- lib/lp/registry/doc/distroseries.txt 2012-09-27 02:53:00 +0000 |
652 | +++ lib/lp/registry/doc/distroseries.txt 2012-09-28 14:42:25 +0000 |
653 | @@ -850,14 +850,14 @@ |
654 | First, there should be one informational specs for krunch: |
655 | |
656 | >>> filter = [SpecificationFilter.INFORMATIONAL] |
657 | - >>> krunch.specifications(filter=filter).count() |
658 | + >>> krunch.specifications(None, filter=filter).count() |
659 | 1 |
660 | |
661 | |
662 | There are 2 completed specs for Krunch: |
663 | |
664 | >>> filter = [SpecificationFilter.COMPLETE] |
665 | - >>> for spec in kubuntu.specifications(filter=filter): |
666 | + >>> for spec in kubuntu.specifications(None, filter=filter): |
667 | ... print spec.name, spec.is_complete |
668 | thinclient-local-devices True |
669 | usplash-on-hibernation True |
670 | @@ -866,7 +866,7 @@ |
671 | And there are 2 incomplete specs: |
672 | |
673 | >>> filter = [SpecificationFilter.INCOMPLETE] |
674 | - >>> for spec in krunch.specifications(filter=filter): |
675 | + >>> for spec in krunch.specifications(None, filter=filter): |
676 | ... print spec.name, spec.is_complete |
677 | cluster-installation False |
678 | revu False |
679 | @@ -875,7 +875,7 @@ |
680 | If we ask for all specs, we get them in the order of priority. |
681 | |
682 | >>> filter = [SpecificationFilter.ALL] |
683 | - >>> for spec in krunch.specifications(filter=filter): |
684 | + >>> for spec in krunch.specifications(None, filter=filter): |
685 | ... print spec.priority.title, spec.name |
686 | Essential cluster-installation |
687 | High revu |
688 | @@ -888,7 +888,7 @@ |
689 | With a distroseries, we can ask for ACCEPTED, PROPOSED and DECLINED specs: |
690 | |
691 | >>> filter=[SpecificationFilter.ACCEPTED] |
692 | - >>> for spec in krunch.specifications(filter=filter): |
693 | + >>> for spec in krunch.specifications(None, filter=filter): |
694 | ... print spec.name, spec.goalstatus.title |
695 | cluster-installation Accepted |
696 | revu Accepted |
697 | @@ -896,12 +896,12 @@ |
698 | usplash-on-hibernation Accepted |
699 | |
700 | >>> filter=[SpecificationFilter.PROPOSED] |
701 | - >>> for spec in krunch.specifications(filter=filter): |
702 | + >>> for spec in krunch.specifications(None, filter=filter): |
703 | ... print spec.name, spec.goalstatus.title |
704 | kde-desktopfile-langpacks Proposed |
705 | |
706 | >>> filter=[SpecificationFilter.DECLINED] |
707 | - >>> for spec in krunch.specifications(filter=filter): |
708 | + >>> for spec in krunch.specifications(None, filter=filter): |
709 | ... print spec.name, spec.goalstatus.title |
710 | krunch-desktop-plan Declined |
711 | |
712 | @@ -909,7 +909,7 @@ |
713 | And if we ask just for specs, we get BOTH the incomplete and the complete |
714 | ones that have been accepted. |
715 | |
716 | - >>> for spec in krunch.specifications(): |
717 | + >>> for spec in krunch.specifications(None): |
718 | ... print spec.name, spec.is_complete, spec.goalstatus.title |
719 | cluster-installation False Accepted |
720 | revu False Accepted |
721 | @@ -918,7 +918,7 @@ |
722 | |
723 | We can filter for specifications that contain specific text: |
724 | |
725 | - >>> for spec in krunch.specifications(filter=['usb']): |
726 | + >>> for spec in krunch.specifications(None, filter=['usb']): |
727 | ... print spec.name |
728 | thinclient-local-devices |
729 | |
730 | |
731 | === modified file 'lib/lp/registry/doc/person-account.txt' |
732 | --- lib/lp/registry/doc/person-account.txt 2012-09-07 18:06:37 +0000 |
733 | +++ lib/lp/registry/doc/person-account.txt 2012-09-28 14:42:25 +0000 |
734 | @@ -70,7 +70,7 @@ |
735 | >>> personset = getUtility(IPersonSet) |
736 | >>> foobar_preferredemail = emailset.getByEmail('foo.bar@canonical.com') |
737 | >>> foobar = personset.get(foobar_preferredemail.personID) |
738 | - >>> foobar.specifications().count() > 0 |
739 | + >>> foobar.specifications(None).count() > 0 |
740 | True |
741 | |
742 | >>> from lp.blueprints.model.specification import Specification |
743 | |
744 | === modified file 'lib/lp/registry/doc/person.txt' |
745 | --- lib/lp/registry/doc/person.txt 2012-09-26 07:45:31 +0000 |
746 | +++ lib/lp/registry/doc/person.txt 2012-09-28 14:42:25 +0000 |
747 | @@ -1146,7 +1146,7 @@ |
748 | him: |
749 | |
750 | >>> from lp.blueprints.enums import SpecificationFilter |
751 | - >>> carlos.specifications(filter=[ |
752 | + >>> carlos.specifications(None, filter=[ |
753 | ... SpecificationFilter.ASSIGNEE, |
754 | ... SpecificationFilter.COMPLETE]).count() |
755 | 0 |
756 | @@ -1154,7 +1154,7 @@ |
757 | Next, Carlos has two incomplete specs *related* to him: |
758 | |
759 | >>> filter = [] |
760 | - >>> for spec in carlos.specifications(filter=filter): |
761 | + >>> for spec in carlos.specifications(None, filter=filter): |
762 | ... print spec.name, spec.is_complete, spec.informational |
763 | svg-support False False |
764 | extension-manager-upgrades False True |
765 | @@ -1177,39 +1177,39 @@ |
766 | |
767 | >>> mark = getUtility(IPersonSet).getByName('mark') |
768 | >>> filter = [SpecificationFilter.APPROVER] |
769 | - >>> for spec in mark.specifications(filter=filter): |
770 | + >>> for spec in mark.specifications(None, filter=filter): |
771 | ... print spec.name |
772 | extension-manager-upgrades |
773 | |
774 | But has registered 5 of them: |
775 | |
776 | >>> filter = [SpecificationFilter.CREATOR] |
777 | - >>> print foobar.specifications(filter=filter).count() |
778 | + >>> print foobar.specifications(None, filter=filter).count() |
779 | 5 |
780 | |
781 | Now Celso, on the other hand, has 2 specs related to him: |
782 | |
783 | >>> cprov = personset.getByName('cprov') |
784 | - >>> cprov.specifications().count() |
785 | + >>> cprov.specifications(None).count() |
786 | 2 |
787 | |
788 | On one of those, he is the approver: |
789 | |
790 | >>> filter = [SpecificationFilter.APPROVER] |
791 | - >>> for spec in cprov.specifications(filter=filter): |
792 | + >>> for spec in cprov.specifications(None, filter=filter): |
793 | ... print spec.name |
794 | svg-support |
795 | |
796 | And on another one, he is the drafter |
797 | |
798 | >>> filter = [SpecificationFilter.DRAFTER] |
799 | - >>> for spec in cprov.specifications(filter=filter): |
800 | + >>> for spec in cprov.specifications(None, filter=filter): |
801 | ... print spec.name |
802 | e4x |
803 | |
804 | We can filter for specifications that contain specific text: |
805 | |
806 | - >>> for spec in cprov.specifications(filter=['svg']): |
807 | + >>> for spec in cprov.specifications(None, filter=['svg']): |
808 | ... print spec.name |
809 | svg-support |
810 | |
811 | @@ -1225,7 +1225,7 @@ |
812 | >>> unlink_source_packages(firefox) |
813 | >>> firefox.active = False |
814 | >>> flush_database_updates() |
815 | - >>> cprov.specifications(filter=['svg']).count() |
816 | + >>> cprov.specifications(None, filter=['svg']).count() |
817 | 0 |
818 | |
819 | Reset firefox so we don't mess up later tests. |
820 | |
821 | === modified file 'lib/lp/registry/doc/product.txt' |
822 | --- lib/lp/registry/doc/product.txt 2012-09-25 16:30:05 +0000 |
823 | +++ lib/lp/registry/doc/product.txt 2012-09-28 14:42:25 +0000 |
824 | @@ -384,7 +384,7 @@ |
825 | First, there should be only one informational spec for firefox: |
826 | |
827 | >>> filter = [SpecificationFilter.INFORMATIONAL] |
828 | - >>> for spec in firefox.specifications(filter=filter): |
829 | + >>> for spec in firefox.specifications(None, filter=filter): |
830 | ... print spec.name |
831 | extension-manager-upgrades |
832 | |
833 | @@ -392,19 +392,19 @@ |
834 | There are no completed specs for firefox: |
835 | |
836 | >>> filter = [SpecificationFilter.COMPLETE] |
837 | - >>> for spec in firefox.specifications(filter=filter): |
838 | + >>> for spec in firefox.specifications(None, filter=filter): |
839 | ... print spec.name |
840 | |
841 | |
842 | And there are five incomplete specs: |
843 | |
844 | >>> filter = [SpecificationFilter.INCOMPLETE] |
845 | - >>> firefox.specifications(filter=filter).count() |
846 | + >>> firefox.specifications(None, filter=filter).count() |
847 | 5 |
848 | |
849 | We can filter for specifications that contain specific text: |
850 | |
851 | - >>> for spec in firefox.specifications(filter=['new']): |
852 | + >>> for spec in firefox.specifications(None, filter=['new']): |
853 | ... print spec.name |
854 | canvas |
855 | e4x |
856 | |
857 | === modified file 'lib/lp/registry/doc/productseries.txt' |
858 | --- lib/lp/registry/doc/productseries.txt 2012-04-10 14:01:17 +0000 |
859 | +++ lib/lp/registry/doc/productseries.txt 2012-09-28 14:42:25 +0000 |
860 | @@ -229,7 +229,7 @@ |
861 | If we ask for ALL specs we should see them both. |
862 | |
863 | >>> filter = [SpecificationFilter.ALL] |
864 | - >>> for s in onezero.specifications(filter=filter): |
865 | + >>> for s in onezero.specifications(None, filter=filter): |
866 | ... print s.name |
867 | a |
868 | b |
869 | @@ -238,23 +238,23 @@ |
870 | specs: |
871 | |
872 | >>> filter=[SpecificationFilter.ACCEPTED] |
873 | - >>> for spec in onezero.specifications(filter=filter): |
874 | + >>> for spec in onezero.specifications(None, filter=filter): |
875 | ... print spec.name, spec.goalstatus.title |
876 | a Accepted |
877 | |
878 | >>> filter=[SpecificationFilter.PROPOSED] |
879 | - >>> onezero.specifications(filter=filter).count() |
880 | + >>> onezero.specifications(None, filter=filter).count() |
881 | 0 |
882 | |
883 | >>> filter=[SpecificationFilter.DECLINED] |
884 | - >>> onezero.specifications(filter=filter).count() |
885 | + >>> onezero.specifications(None, filter=filter).count() |
886 | 1 |
887 | |
888 | We should see one informational spec if we ask just for that, the |
889 | accepted one. |
890 | |
891 | >>> filter = [SpecificationFilter.INFORMATIONAL] |
892 | - >>> for s in onezero.specifications(filter=filter): |
893 | + >>> for s in onezero.specifications(None, filter=filter): |
894 | ... print s.name |
895 | a |
896 | |
897 | @@ -262,21 +262,21 @@ |
898 | |
899 | >>> filter = [ |
900 | ... SpecificationFilter.INFORMATIONAL, SpecificationFilter.DECLINED] |
901 | - >>> for s in onezero.specifications(filter=filter): |
902 | + >>> for s in onezero.specifications(None, filter=filter): |
903 | ... print s.name |
904 | b |
905 | |
906 | There are is one completed, accepted spec for 1.0: |
907 | |
908 | >>> filter = [SpecificationFilter.COMPLETE] |
909 | - >>> for spec in onezero.specifications(filter=filter): |
910 | + >>> for spec in onezero.specifications(None, filter=filter): |
911 | ... print spec.name, spec.is_complete, spec.goalstatus.title |
912 | a True Accepted |
913 | |
914 | There is one completed, declined spec: |
915 | |
916 | >>> filter = [SpecificationFilter.COMPLETE, SpecificationFilter.DECLINED] |
917 | - >>> for spec in onezero.specifications(filter=filter): |
918 | + >>> for spec in onezero.specifications(None, filter=filter): |
919 | ... print spec.name, spec.is_complete, spec.goalstatus.title |
920 | b True Declined |
921 | |
922 | @@ -291,7 +291,7 @@ |
923 | And if we ask just for specs, we get BOTH the incomplete and the |
924 | complete ones that have been accepted. |
925 | |
926 | - >>> for spec in onezero.specifications(): |
927 | + >>> for spec in onezero.specifications(None): |
928 | ... print spec.name, spec.is_complete, spec.goalstatus.title |
929 | a True Accepted |
930 | b False Accepted |
931 | @@ -299,7 +299,7 @@ |
932 | We can search for text in specifications (in this case there are no |
933 | matches): |
934 | |
935 | - >>> print len(list(onezero.specifications(filter=['new']))) |
936 | + >>> print len(list(onezero.specifications(None, filter=['new']))) |
937 | 0 |
938 | |
939 | |
940 | |
941 | === modified file 'lib/lp/registry/doc/projectgroup.txt' |
942 | --- lib/lp/registry/doc/projectgroup.txt 2012-09-28 14:42:25 +0000 |
943 | +++ lib/lp/registry/doc/projectgroup.txt 2012-09-28 14:42:25 +0000 |
944 | @@ -150,7 +150,7 @@ |
945 | First, there should be only one informational spec for mozilla: |
946 | |
947 | >>> filter = [SpecificationFilter.INFORMATIONAL] |
948 | - >>> for spec in mozilla.specifications(filter=filter): |
949 | + >>> for spec in mozilla.specifications(None, filter=filter): |
950 | ... print spec.name |
951 | extension-manager-upgrades |
952 | |
953 | @@ -158,19 +158,19 @@ |
954 | There are no completed specs for mozilla: |
955 | |
956 | >>> filter = [SpecificationFilter.COMPLETE] |
957 | - >>> for spec in mozilla.specifications(filter=filter): |
958 | + >>> for spec in mozilla.specifications(None, filter=filter): |
959 | ... print spec.name |
960 | |
961 | |
962 | And there are five incomplete specs: |
963 | |
964 | >>> filter = [SpecificationFilter.INCOMPLETE] |
965 | - >>> mozilla.specifications(filter=filter).count() |
966 | + >>> mozilla.specifications(None, filter=filter).count() |
967 | 5 |
968 | |
969 | We can filter for specifications that contain specific text: |
970 | |
971 | - >>> for spec in mozilla.specifications(filter=['install']): |
972 | + >>> for spec in mozilla.specifications(None, filter=['install']): |
973 | ... print spec.name |
974 | extension-manager-upgrades |
975 | |
976 | @@ -178,7 +178,7 @@ |
977 | Inactive products are excluded from the listings. |
978 | |
979 | >>> filter = [SpecificationFilter.INCOMPLETE] |
980 | - >>> mozilla.specifications(filter=filter).count() |
981 | + >>> mozilla.specifications(None, filter=filter).count() |
982 | 5 |
983 | |
984 | >>> from lp.registry.interfaces.product import IProductSet |
985 | @@ -190,7 +190,7 @@ |
986 | >>> firefox.active = False |
987 | >>> flush_database_updates() |
988 | >>> filter = [SpecificationFilter.INCOMPLETE] |
989 | - >>> mozilla.specifications(filter=filter).count() |
990 | + >>> mozilla.specifications(None, filter=filter).count() |
991 | 0 |
992 | |
993 | Reset firefox so we don't mess up later tests. |
994 | @@ -248,7 +248,8 @@ |
995 | mozilla_1_0_series._all_specifications. |
996 | |
997 | >>> filter = [SpecificationFilter.INFORMATIONAL] |
998 | - >>> extension_manager_upgrades = mozilla.specifications(filter=filter)[0] |
999 | + >>> extension_manager_upgrades = mozilla.specifications( |
1000 | + ... None, filter=filter)[0] |
1001 | >>> series_1_0 = firefox.getSeries('1.0') |
1002 | >>> extension_manager_upgrades.proposeGoal(series_1_0, no_priv) |
1003 | >>> for spec in mozilla_series_1_0._all_specifications: |
1004 | @@ -264,7 +265,7 @@ |
1005 | Filtered lists of project series related specifications are generated |
1006 | the same way as for project related specifications. |
1007 | |
1008 | - >>> for spec in mozilla_series_1_0.specifications(filter=filter): |
1009 | + >>> for spec in mozilla_series_1_0.specifications(None, filter=filter): |
1010 | ... print spec.name |
1011 | extension-manager-upgrades |
1012 | |
1013 | @@ -277,7 +278,7 @@ |
1014 | project itself. |
1015 | |
1016 | >>> filter = [SpecificationFilter.INCOMPLETE] |
1017 | - >>> for spec in mozilla_series_1_0.specifications(filter=filter): |
1018 | + >>> for spec in mozilla_series_1_0.specifications(None, filter=filter): |
1019 | ... print spec.name |
1020 | svg-support |
1021 | canvas |
1022 | @@ -287,21 +288,22 @@ |
1023 | |
1024 | Searching for text is also possible. |
1025 | |
1026 | - >>> for spec in mozilla_series_1_0.specifications(filter=['install']): |
1027 | + >>> for spec in mozilla_series_1_0.specifications( |
1028 | + ... None, filter=['install']): |
1029 | ... print spec.name |
1030 | extension-manager-upgrades |
1031 | |
1032 | Inactive products are excluded from the series listings. |
1033 | |
1034 | >>> filter = [SpecificationFilter.INCOMPLETE] |
1035 | - >>> specs = mozilla_series_1_0.specifications(filter=filter) |
1036 | + >>> specs = mozilla_series_1_0.specifications(None, filter=filter) |
1037 | >>> print specs.count() |
1038 | 5 |
1039 | |
1040 | >>> firefox = getUtility(IProductSet).getByName('firefox') |
1041 | >>> firefox.active = False |
1042 | >>> filter = [SpecificationFilter.INCOMPLETE] |
1043 | - >>> mozilla_series_1_0.specifications(filter=filter).count() |
1044 | + >>> mozilla_series_1_0.specifications(None, filter=filter).count() |
1045 | 0 |
1046 | |
1047 | Reset firefox so we don't mess up later tests. |
1048 | |
1049 | === modified file 'lib/lp/registry/model/distribution.py' |
1050 | --- lib/lp/registry/model/distribution.py 2012-09-28 14:42:25 +0000 |
1051 | +++ lib/lp/registry/model/distribution.py 2012-09-28 14:42:25 +0000 |
1052 | @@ -878,7 +878,7 @@ |
1053 | return getUtility(IDistributionSet).getCurrentSourceReleases( |
1054 | {self: source_package_names}) |
1055 | |
1056 | - def specifications(self, sort=None, quantity=None, filter=None, |
1057 | + def specifications(self, user, sort=None, quantity=None, filter=None, |
1058 | prejoin_people=True): |
1059 | """See `IHasSpecifications`. |
1060 | |
1061 | |
1062 | === modified file 'lib/lp/registry/model/distroseries.py' |
1063 | --- lib/lp/registry/model/distroseries.py 2012-09-28 14:42:25 +0000 |
1064 | +++ lib/lp/registry/model/distroseries.py 2012-09-28 14:42:25 +0000 |
1065 | @@ -777,7 +777,7 @@ |
1066 | """See `IHasBugs`.""" |
1067 | return self.distribution.official_bug_tags |
1068 | |
1069 | - def specifications(self, sort=None, quantity=None, filter=None, |
1070 | + def specifications(self, user, sort=None, quantity=None, filter=None, |
1071 | prejoin_people=True): |
1072 | """See IHasSpecifications. |
1073 | |
1074 | |
1075 | === modified file 'lib/lp/registry/model/person.py' |
1076 | --- lib/lp/registry/model/person.py 2012-09-28 14:42:25 +0000 |
1077 | +++ lib/lp/registry/model/person.py 2012-09-28 14:42:25 +0000 |
1078 | @@ -821,7 +821,7 @@ |
1079 | """See `IPerson`.""" |
1080 | return "%s (%s)" % (self.displayname, self.name) |
1081 | |
1082 | - def specifications(self, sort=None, quantity=None, filter=None, |
1083 | + def specifications(self, user, sort=None, quantity=None, filter=None, |
1084 | prejoin_people=True): |
1085 | """See `IHasSpecifications`.""" |
1086 | |
1087 | |
1088 | === modified file 'lib/lp/registry/model/product.py' |
1089 | --- lib/lp/registry/model/product.py 2012-09-28 14:42:25 +0000 |
1090 | +++ lib/lp/registry/model/product.py 2012-09-28 14:42:25 +0000 |
1091 | @@ -1303,7 +1303,7 @@ |
1092 | # automatically shared. |
1093 | return True |
1094 | |
1095 | - def specifications(self, sort=None, quantity=None, filter=None, |
1096 | + def specifications(self, user, sort=None, quantity=None, filter=None, |
1097 | prejoin_people=True): |
1098 | """See `IHasSpecifications`.""" |
1099 | |
1100 | |
1101 | === modified file 'lib/lp/registry/model/productseries.py' |
1102 | --- lib/lp/registry/model/productseries.py 2012-09-28 14:42:25 +0000 |
1103 | +++ lib/lp/registry/model/productseries.py 2012-09-28 14:42:25 +0000 |
1104 | @@ -301,7 +301,7 @@ |
1105 | """See `IProductSeries`.""" |
1106 | return self == self.product.development_focus |
1107 | |
1108 | - def specifications(self, sort=None, quantity=None, filter=None, |
1109 | + def specifications(self, user, sort=None, quantity=None, filter=None, |
1110 | prejoin_people=True): |
1111 | """See IHasSpecifications. |
1112 | |
1113 | |
1114 | === modified file 'lib/lp/registry/model/projectgroup.py' |
1115 | --- lib/lp/registry/model/projectgroup.py 2012-09-28 14:42:25 +0000 |
1116 | +++ lib/lp/registry/model/projectgroup.py 2012-09-28 14:42:25 +0000 |
1117 | @@ -231,7 +231,7 @@ |
1118 | """ % sqlvalues(self, SprintSpecificationStatus.ACCEPTED) |
1119 | return query, ['Product', 'Specification', 'SprintSpecification'] |
1120 | |
1121 | - def specifications(self, sort=None, quantity=None, filter=None, |
1122 | + def specifications(self, user, sort=None, quantity=None, filter=None, |
1123 | series=None, prejoin_people=True): |
1124 | """See `IHasSpecifications`.""" |
1125 | |
1126 | @@ -631,10 +631,11 @@ |
1127 | self.project = project |
1128 | self.name = name |
1129 | |
1130 | - def specifications(self, sort=None, quantity=None, filter=None, |
1131 | + def specifications(self, user, sort=None, quantity=None, filter=None, |
1132 | prejoin_people=True): |
1133 | return self.project.specifications( |
1134 | - sort, quantity, filter, self.name, prejoin_people=prejoin_people) |
1135 | + user, sort, quantity, filter, self.name, |
1136 | + prejoin_people=prejoin_people) |
1137 | |
1138 | @property |
1139 | def title(self): |
1140 | |
1141 | === modified file 'lib/lp/testing/factory.py' |
1142 | --- lib/lp/testing/factory.py 2012-09-28 06:25:44 +0000 |
1143 | +++ lib/lp/testing/factory.py 2012-09-28 14:42:25 +0000 |
1144 | @@ -2091,8 +2091,16 @@ |
1145 | :param product: The product to make the blueprint on. If one is |
1146 | not specified, an arbitrary product is created. |
1147 | """ |
1148 | + proprietary = (information_type not in PUBLIC_INFORMATION_TYPES and |
1149 | + information_type is not None) |
1150 | if distribution is None and product is None: |
1151 | - product = self.makeProduct() |
1152 | + if proprietary: |
1153 | + specification_sharing_policy = ( |
1154 | + SpecificationSharingPolicy.EMBARGOED_OR_PROPRIETARY) |
1155 | + else: |
1156 | + specification_sharing_policy = None |
1157 | + product = self.makeProduct( |
1158 | + specification_sharing_policy=specification_sharing_policy) |
1159 | if name is None: |
1160 | name = self.getUniqueString('name') |
1161 | if summary is None: |
1162 | @@ -2125,7 +2133,7 @@ |
1163 | priority=priority) |
1164 | naked_spec = removeSecurityProxy(spec) |
1165 | if information_type is not None: |
1166 | - if information_type not in PUBLIC_INFORMATION_TYPES: |
1167 | + if proprietary: |
1168 | naked_spec.target._ensurePolicies([information_type]) |
1169 | naked_spec.transitionToInformationType( |
1170 | information_type, spec.target.owner) |
1171 | @@ -4340,9 +4348,9 @@ |
1172 | return link |
1173 | |
1174 | def makeAccessArtifactGrant(self, artifact=None, grantee=None, |
1175 | - grantor=None): |
1176 | + grantor=None, concrete_artifact=None): |
1177 | if artifact is None: |
1178 | - artifact = self.makeAccessArtifact() |
1179 | + artifact = self.makeAccessArtifact(concrete_artifact) |
1180 | if grantee is None: |
1181 | grantee = self.makePerson() |
1182 | if grantor is None: |
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.