Merge lp:~stevenk/launchpad/new-branch-visibility into lp:launchpad
- new-branch-visibility
- Merge into devel
Proposed by
Steve Kowalik
Status: | Merged |
---|---|
Approved by: | William Grant |
Approved revision: | no longer in the source branch. |
Merged at revision: | 15570 |
Proposed branch: | lp:~stevenk/launchpad/new-branch-visibility |
Merge into: | lp:launchpad |
Diff against target: |
559 lines (+104/-116) 17 files modified
database/sampledata/current-dev.sql (+1/-0) database/sampledata/current.sql (+1/-0) database/schema/security.cfg (+1/-0) lib/lp/code/browser/tests/test_branch.py (+1/-1) lib/lp/code/browser/tests/test_branchmergeproposal.py (+3/-2) lib/lp/code/browser/tests/test_product.py (+5/-4) lib/lp/code/model/branch.py (+58/-19) lib/lp/code/model/branchcollection.py (+7/-48) lib/lp/code/model/tests/test_branchcollection.py (+0/-14) lib/lp/code/model/tests/test_branchmergeproposal.py (+7/-7) lib/lp/code/model/tests/test_branchsubscription.py (+1/-1) lib/lp/code/stories/branches/xx-branch-listings.txt (+7/-6) lib/lp/code/stories/feeds/xx-branch-atom.txt (+1/-2) lib/lp/code/tests/test_branch_access_policy_triggers.py (+4/-3) lib/lp/registry/browser/tests/test_pillar_sharing.py (+3/-4) lib/lp/registry/stories/product/xx-product-development-focus.txt (+2/-3) lib/lp/security.py (+2/-2) |
To merge this branch: | bzr merge lp:~stevenk/launchpad/new-branch-visibility |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Review via email: mp+113484@code.launchpad.net |
Commit message
Switch BranchCollectio
Description of the change
Switch IBranch.
To post a comment you must log in.
Revision history for this message
William Grant (wgrant) : | # |
review:
Approve
(code)
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'database/sampledata/current-dev.sql' |
2 | --- database/sampledata/current-dev.sql 2012-07-04 22:38:14 +0000 |
3 | +++ database/sampledata/current-dev.sql 2012-07-06 13:00:27 +0000 |
4 | @@ -3131,6 +3131,7 @@ |
5 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (2, 12, 24, '2006-10-16 18:31:43.080236', 1, NULL, 0, 12); |
6 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (4, 64, 29, '2007-05-28 02:41:07.938677', 1, NULL, 0, 64); |
7 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (5, 64, 30, '2007-05-28 02:41:07.938677', 1, NULL, 0, 64); |
8 | +INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (6, 12, 1, '2006-10-16 18:31:43.099585', 1, NULL, 0, 12); |
9 | |
10 | |
11 | ALTER TABLE branchsubscription ENABLE TRIGGER ALL; |
12 | |
13 | === modified file 'database/sampledata/current.sql' |
14 | --- database/sampledata/current.sql 2012-07-04 22:38:14 +0000 |
15 | +++ database/sampledata/current.sql 2012-07-06 13:00:27 +0000 |
16 | @@ -3068,6 +3068,7 @@ |
17 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (2, 12, 24, '2006-10-16 18:31:43.080236', 1, NULL, 0, 12); |
18 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (4, 64, 29, '2007-05-28 02:41:07.938677', 1, NULL, 0, 64); |
19 | INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (5, 64, 30, '2007-05-28 02:41:07.938677', 1, NULL, 0, 64); |
20 | +INSERT INTO branchsubscription (id, person, branch, date_created, notification_level, max_diff_lines, review_level, subscribed_by) VALUES (6, 12, 1, '2006-10-16 18:31:43.099585', 1, NULL, 0, 12); |
21 | |
22 | |
23 | ALTER TABLE branchsubscription ENABLE TRIGGER ALL; |
24 | |
25 | === modified file 'database/schema/security.cfg' |
26 | --- database/schema/security.cfg 2012-07-05 10:54:35 +0000 |
27 | +++ database/schema/security.cfg 2012-07-06 13:00:27 +0000 |
28 | @@ -693,6 +693,7 @@ |
29 | public.accessartifact = SELECT, INSERT, DELETE |
30 | public.accesspolicy = SELECT |
31 | public.accesspolicyartifact = SELECT, INSERT, DELETE |
32 | +public.accesspolicygrant = SELECT |
33 | public.branch = SELECT, INSERT, UPDATE |
34 | public.branchrevision = SELECT, INSERT |
35 | public.branchsubscription = SELECT, INSERT |
36 | |
37 | === modified file 'lib/lp/code/browser/tests/test_branch.py' |
38 | --- lib/lp/code/browser/tests/test_branch.py 2012-06-22 05:52:17 +0000 |
39 | +++ lib/lp/code/browser/tests/test_branch.py 2012-07-06 13:00:27 +0000 |
40 | @@ -627,7 +627,7 @@ |
41 | with person_logged_in(branch.owner): |
42 | self.factory.makeBranchMergeProposal( |
43 | source_branch=branch, target_branch=target_branch, |
44 | - reviewer=private_reviewer) |
45 | + reviewer=removeSecurityProxy(private_reviewer)) |
46 | return branch |
47 | |
48 | def test_view_branch_with_private_reviewer(self): |
49 | |
50 | === modified file 'lib/lp/code/browser/tests/test_branchmergeproposal.py' |
51 | --- lib/lp/code/browser/tests/test_branchmergeproposal.py 2012-06-08 06:01:50 +0000 |
52 | +++ lib/lp/code/browser/tests/test_branchmergeproposal.py 2012-07-06 13:00:27 +0000 |
53 | @@ -1296,8 +1296,9 @@ |
54 | bmp2 = self.factory.makeBranchMergeProposal( |
55 | date_created=( |
56 | datetime(year=2008, month=10, day=10, tzinfo=pytz.UTC))) |
57 | - removeSecurityProxy(bmp2.source_branch).information_type = ( |
58 | - InformationType.USERDATA) |
59 | + removeSecurityProxy(bmp2.source_branch).transitionToInformationType( |
60 | + InformationType.USERDATA, bmp2.source_branch.owner, |
61 | + verify_policy=False) |
62 | self.assertEqual( |
63 | [bmp1], latest_proposals_for_each_branch([bmp1, bmp2])) |
64 | |
65 | |
66 | === modified file 'lib/lp/code/browser/tests/test_product.py' |
67 | --- lib/lp/code/browser/tests/test_product.py 2012-05-31 03:54:13 +0000 |
68 | +++ lib/lp/code/browser/tests/test_product.py 2012-07-06 13:00:27 +0000 |
69 | @@ -112,15 +112,16 @@ |
70 | |
71 | def test_initial_branches_contains_dev_focus_branch(self): |
72 | product, branch = self.makeProductAndDevelopmentFocusBranch() |
73 | - view = create_initialized_view(product, '+code-index', |
74 | - rootsite='code') |
75 | + view = create_initialized_view( |
76 | + product, '+code-index', rootsite='code') |
77 | self.assertIn(branch, view.initial_branches) |
78 | |
79 | def test_initial_branches_does_not_contain_private_dev_focus_branch(self): |
80 | product, branch = self.makeProductAndDevelopmentFocusBranch( |
81 | information_type=InformationType.USERDATA) |
82 | - view = create_initialized_view(product, '+code-index', |
83 | - rootsite='code') |
84 | + login(ANONYMOUS) |
85 | + view = create_initialized_view( |
86 | + product, '+code-index', rootsite='code') |
87 | self.assertNotIn(branch, view.initial_branches) |
88 | |
89 | def test_committer_count_with_revision_authors(self): |
90 | |
91 | === modified file 'lib/lp/code/model/branch.py' |
92 | --- lib/lp/code/model/branch.py 2012-06-27 22:31:38 +0000 |
93 | +++ lib/lp/code/model/branch.py 2012-07-06 13:00:27 +0000 |
94 | @@ -7,6 +7,7 @@ |
95 | __all__ = [ |
96 | 'Branch', |
97 | 'BranchSet', |
98 | + 'get_branch_privacy_filter', |
99 | ] |
100 | |
101 | from datetime import datetime |
102 | @@ -25,9 +26,11 @@ |
103 | ) |
104 | from storm.expr import ( |
105 | And, |
106 | + Coalesce, |
107 | Count, |
108 | Desc, |
109 | Insert, |
110 | + Join, |
111 | NamedFunc, |
112 | Not, |
113 | Or, |
114 | @@ -36,6 +39,7 @@ |
115 | from storm.locals import ( |
116 | AutoReload, |
117 | Int, |
118 | + List, |
119 | Reference, |
120 | ) |
121 | from storm.store import Store |
122 | @@ -145,7 +149,11 @@ |
123 | validate_person, |
124 | validate_public_person, |
125 | ) |
126 | -from lp.registry.model.accesspolicy import reconcile_access_for_artifact |
127 | +from lp.registry.model.accesspolicy import ( |
128 | + AccessPolicyGrant, |
129 | + reconcile_access_for_artifact, |
130 | + ) |
131 | +from lp.registry.model.teammembership import TeamParticipation |
132 | from lp.services.config import config |
133 | from lp.services.database.bulk import load_related |
134 | from lp.services.database.constants import ( |
135 | @@ -160,6 +168,10 @@ |
136 | SQLBase, |
137 | sqlvalues, |
138 | ) |
139 | +from lp.services.database.stormexpr import ( |
140 | + ArrayAgg, |
141 | + ArrayIntersects, |
142 | + ) |
143 | from lp.services.helpers import shortlist |
144 | from lp.services.job.interfaces.job import JobStatus |
145 | from lp.services.job.model.job import Job |
146 | @@ -188,6 +200,8 @@ |
147 | mirror_status_message = StringCol(default=None) |
148 | information_type = EnumCol( |
149 | enum=InformationType, default=InformationType.PUBLIC) |
150 | + access_policy = IntCol() |
151 | + access_grants = List(type=Int()) |
152 | |
153 | # These can die after the UI is dropped. |
154 | @property |
155 | @@ -243,6 +257,15 @@ |
156 | raise BranchCannotBePublic() |
157 | self.information_type = information_type |
158 | self._reconcileAccess() |
159 | + if information_type in PRIVATE_INFORMATION_TYPES: |
160 | + # Grant the subscriber access if they can't see the branch. |
161 | + service = getUtility(IService, 'sharing') |
162 | + blind_subscribers = service.getPeopleWithoutAccess( |
163 | + self, self.subscribers) |
164 | + if len(blind_subscribers): |
165 | + service.ensureAccessGrants( |
166 | + blind_subscribers, who, branches=[self], |
167 | + ignore_permissions=True) |
168 | |
169 | registrant = ForeignKey( |
170 | dbName='registrant', foreignKey='Person', |
171 | @@ -1257,28 +1280,15 @@ |
172 | job.celeryRunOnCommit() |
173 | return job |
174 | |
175 | - def _checkBranchVisibleByUser(self, user): |
176 | - """Is *this* branch visible by the user. |
177 | - |
178 | - This method doesn't check the stacked upon branch. That is handled by |
179 | - the `visibleByUser` method. |
180 | - """ |
181 | - if self.information_type in PUBLIC_INFORMATION_TYPES: |
182 | - return True |
183 | - if user is None: |
184 | - return False |
185 | - if user.inTeam(self.owner): |
186 | - return True |
187 | - for subscriber in self.subscribers: |
188 | - if user.inTeam(subscriber): |
189 | - return True |
190 | - return user_has_special_branch_access(user) |
191 | - |
192 | def visibleByUser(self, user, checked_branches=None): |
193 | """See `IBranch`.""" |
194 | if checked_branches is None: |
195 | checked_branches = [] |
196 | - can_access = self._checkBranchVisibleByUser(user) |
197 | + if self.information_type in PUBLIC_INFORMATION_TYPES: |
198 | + can_access = True |
199 | + else: |
200 | + can_access = not getUtility(IAllBranches).withIds( |
201 | + self.id).visibleByUser(user).is_empty() |
202 | if can_access and self.stacked_on is not None: |
203 | checked_branches.append(self) |
204 | if self.stacked_on not in checked_branches: |
205 | @@ -1518,3 +1528,32 @@ |
206 | """ |
207 | update_trigger_modified_fields(branch) |
208 | send_branch_modified_notifications(branch, event) |
209 | + |
210 | + |
211 | +def get_branch_privacy_filter(user, branch_class=Branch): |
212 | + public_branch_filter = ( |
213 | + branch_class.information_type.is_in(PUBLIC_INFORMATION_TYPES)) |
214 | + |
215 | + if user is None: |
216 | + return [public_branch_filter] |
217 | + |
218 | + artifact_grant_query = Coalesce( |
219 | + ArrayIntersects(branch_class.access_grants, |
220 | + Select( |
221 | + ArrayAgg(TeamParticipation.teamID), |
222 | + tables=TeamParticipation, |
223 | + where=(TeamParticipation.person == user) |
224 | + )), False) |
225 | + |
226 | + policy_grant_query = branch_class.access_policy.is_in( |
227 | + Select( |
228 | + AccessPolicyGrant.policy_id, |
229 | + tables=(AccessPolicyGrant, |
230 | + Join(TeamParticipation, |
231 | + TeamParticipation.teamID == |
232 | + AccessPolicyGrant.grantee_id)), |
233 | + where=(TeamParticipation.person == user) |
234 | + )) |
235 | + |
236 | + return [ |
237 | + Or(public_branch_filter, artifact_grant_query, policy_grant_query)] |
238 | |
239 | === modified file 'lib/lp/code/model/branchcollection.py' |
240 | --- lib/lp/code/model/branchcollection.py 2012-06-06 06:56:17 +0000 |
241 | +++ lib/lp/code/model/branchcollection.py 2012-07-06 13:00:27 +0000 |
242 | @@ -1,4 +1,4 @@ |
243 | -# Copyright 2009-2011 Canonical Ltd. This software is licensed under the |
244 | +# Copyright 2009-2012 Canonical Ltd. This software is licensed under the |
245 | # GNU Affero General Public License version 3 (see the file LICENSE). |
246 | |
247 | """Implementations of `IBranchCollection`.""" |
248 | @@ -49,7 +49,10 @@ |
249 | from lp.code.interfaces.seriessourcepackagebranch import ( |
250 | IFindOfficialBranchLinks, |
251 | ) |
252 | -from lp.code.model.branch import Branch |
253 | +from lp.code.model.branch import ( |
254 | + Branch, |
255 | + get_branch_privacy_filter, |
256 | + ) |
257 | from lp.code.model.branchmergeproposal import BranchMergeProposal |
258 | from lp.code.model.branchsubscription import BranchSubscription |
259 | from lp.code.model.codeimport import CodeImport |
260 | @@ -787,7 +790,6 @@ |
261 | asymmetric_filter_expressions=asymmetric_filter_expressions, |
262 | asymmetric_tables=asymmetric_tables) |
263 | self._user = user |
264 | - self._private_branch_ids = self._getPrivateBranchSubQuery() |
265 | |
266 | def _filterBy(self, expressions, table=None, join=None, |
267 | exclude_from_search=None, symmetric=True): |
268 | @@ -829,57 +831,14 @@ |
269 | asymmetric_expr, |
270 | asymmetric_tables) |
271 | |
272 | - def _getPrivateBranchSubQuery(self): |
273 | - """Return a subquery to get the private branches the user can see. |
274 | - |
275 | - If the user is None (which is used for anonymous access), then there |
276 | - is no subquery. Otherwise return the branch ids for the private |
277 | - branches that the user owns or is subscribed to. |
278 | - """ |
279 | - # Everyone can see public branches. |
280 | - person = self._user |
281 | - if person is None: |
282 | - # Anonymous users can only see the public branches. |
283 | - return None |
284 | - |
285 | - # A union is used here rather than the more simplistic simple joins |
286 | - # due to the query plans generated. If we just have a simple query |
287 | - # then we are joining across TeamParticipation and BranchSubscription. |
288 | - # This creates a bad plan, hence the use of a union. |
289 | - private_branches = Union( |
290 | - # Private branches the person owns (or a team the person is in). |
291 | - Select(Branch.id, |
292 | - And(Branch.owner == TeamParticipation.teamID, |
293 | - TeamParticipation.person == person, |
294 | - Branch.information_type.is_in( |
295 | - PRIVATE_INFORMATION_TYPES))), |
296 | - # Private branches the person is subscribed to, either directly or |
297 | - # indirectly. |
298 | - Select(Branch.id, |
299 | - And(BranchSubscription.branch == Branch.id, |
300 | - BranchSubscription.person == |
301 | - TeamParticipation.teamID, |
302 | - TeamParticipation.person == person, |
303 | - Branch.information_type.is_in( |
304 | - PRIVATE_INFORMATION_TYPES)))) |
305 | - return private_branches |
306 | - |
307 | def _getBranchVisibilityExpression(self, branch_class=Branch): |
308 | """Return the where clauses for visibility. |
309 | |
310 | :param branch_class: The Branch class to use - permits using |
311 | ClassAliases. |
312 | """ |
313 | - public_branches = branch_class.information_type.is_in( |
314 | - PUBLIC_INFORMATION_TYPES) |
315 | - if self._private_branch_ids is None: |
316 | - # Public only. |
317 | - return [public_branches] |
318 | - else: |
319 | - public_or_private = Or( |
320 | - public_branches, |
321 | - branch_class.id.is_in(self._private_branch_ids)) |
322 | - return [public_or_private] |
323 | + return get_branch_privacy_filter( |
324 | + self._user, branch_class=branch_class) |
325 | |
326 | def _getCandidateBranchesWith(self): |
327 | """Return WITH clauses defining candidate branches. |
328 | |
329 | === modified file 'lib/lp/code/model/tests/test_branchcollection.py' |
330 | --- lib/lp/code/model/tests/test_branchcollection.py 2012-06-11 10:25:46 +0000 |
331 | +++ lib/lp/code/model/tests/test_branchcollection.py 2012-07-06 13:00:27 +0000 |
332 | @@ -636,20 +636,6 @@ |
333 | sorted([self.public_branch, self.private_branch1]), |
334 | sorted(branches.getBranches())) |
335 | |
336 | - def test_owner_member_sees_own_branches(self): |
337 | - # Members of teams that own branches can see branches owned by those |
338 | - # teams, as well as public branches. |
339 | - team_owner = self.factory.makePerson() |
340 | - team = self.factory.makeTeam(team_owner) |
341 | - private_branch = self.factory.makeAnyBranch( |
342 | - owner=team, stacked_on=self.private_stacked_on_branch, |
343 | - name='team') |
344 | - removeSecurityProxy(private_branch).unsubscribe(team, team_owner) |
345 | - branches = self.all_branches.visibleByUser(team_owner) |
346 | - self.assertEqual( |
347 | - sorted([self.public_branch, private_branch]), |
348 | - sorted(branches.getBranches())) |
349 | - |
350 | def test_launchpad_services_sees_all(self): |
351 | # The LAUNCHPAD_SERVICES special user sees *everything*. |
352 | branches = self.all_branches.visibleByUser(LAUNCHPAD_SERVICES) |
353 | |
354 | === modified file 'lib/lp/code/model/tests/test_branchmergeproposal.py' |
355 | --- lib/lp/code/model/tests/test_branchmergeproposal.py 2012-06-11 10:25:46 +0000 |
356 | +++ lib/lp/code/model/tests/test_branchmergeproposal.py 2012-07-06 13:00:27 +0000 |
357 | @@ -166,7 +166,7 @@ |
358 | product=product) |
359 | with person_logged_in(owner): |
360 | trunk.reviewer = team |
361 | - bmp = self.factory.makeBranchMergeProposal( |
362 | + self.factory.makeBranchMergeProposal( |
363 | source_branch=branch, target_branch=trunk) |
364 | subscriptions = [bsub.person for bsub in branch.subscriptions] |
365 | self.assertEqual([owner], subscriptions) |
366 | @@ -183,7 +183,7 @@ |
367 | product=product) |
368 | with person_logged_in(owner): |
369 | trunk.reviewer = team |
370 | - bmp = self.factory.makeBranchMergeProposal( |
371 | + self.factory.makeBranchMergeProposal( |
372 | source_branch=branch, target_branch=trunk) |
373 | subscriptions = [bsub.person for bsub in branch.subscriptions] |
374 | self.assertContentEqual([owner, team], subscriptions) |
375 | @@ -967,13 +967,13 @@ |
376 | CodeReviewNotificationLevel.FULL, charlie) |
377 | # Make both branches private. |
378 | for branch in (bmp.source_branch, bmp.target_branch): |
379 | - removeSecurityProxy(branch).information_type = ( |
380 | - InformationType.USERDATA) |
381 | + removeSecurityProxy(branch).transitionToInformationType( |
382 | + InformationType.USERDATA, branch.owner, verify_policy=False) |
383 | recipients = bmp.getNotificationRecipients( |
384 | CodeReviewNotificationLevel.FULL) |
385 | - self.assertFalse(bob in recipients) |
386 | - self.assertFalse(eric in recipients) |
387 | - self.assertTrue(charlie in recipients) |
388 | + self.assertNotIn(bob, recipients) |
389 | + self.assertNotIn(eric, recipients) |
390 | + self.assertIn(charlie, recipients) |
391 | |
392 | |
393 | class TestGetAddress(TestCaseWithFactory): |
394 | |
395 | === modified file 'lib/lp/code/model/tests/test_branchsubscription.py' |
396 | --- lib/lp/code/model/tests/test_branchsubscription.py 2012-06-27 22:31:38 +0000 |
397 | +++ lib/lp/code/model/tests/test_branchsubscription.py 2012-07-06 13:00:27 +0000 |
398 | @@ -95,7 +95,7 @@ |
399 | None, CodeReviewNotificationLevel.NOEMAIL, owner) |
400 | self.assertTrue(branch.visibleByUser(subscribee)) |
401 | |
402 | - def test_unsubscribe_gives_access(self): |
403 | + def test_unsubscribe_removes_access(self): |
404 | """Unsubscibing a user to a branch removes their access.""" |
405 | owner = self.factory.makePerson() |
406 | branch = self.factory.makeBranch( |
407 | |
408 | === modified file 'lib/lp/code/stories/branches/xx-branch-listings.txt' |
409 | --- lib/lp/code/stories/branches/xx-branch-listings.txt 2012-06-22 05:52:17 +0000 |
410 | +++ lib/lp/code/stories/branches/xx-branch-listings.txt 2012-07-06 13:00:27 +0000 |
411 | @@ -25,7 +25,7 @@ |
412 | >>> links = find_tag_by_id(browser.contents, 'branch-batch-links') |
413 | >>> print links.renderContents() |
414 | <BLANKLINE> |
415 | - ...1...→...6...of 10 results... |
416 | + ...1...→...6...of 9 results... |
417 | |
418 | >>> table = find_tag_by_id(browser.contents, 'branchtable') |
419 | >>> for row in table.thead.fetch('tr'): |
420 | @@ -52,13 +52,12 @@ |
421 | >>> links = find_tag_by_id(browser.contents, 'branch-batch-links') |
422 | >>> print links.renderContents() |
423 | <BLANKLINE> |
424 | - ...7...→...10...of 10 results... |
425 | + ...7...→...9...of 9 results... |
426 | |
427 | >>> table = find_tag_by_id(browser.contents, 'branchtable') |
428 | >>> for row in table.tbody.fetch('tr'): |
429 | ... print extract_text(row) |
430 | lp://dev/~name12/gnome-terminal/klingon Experimental ... |
431 | - lp://dev/~name12/landscape/feature-x Development ... |
432 | lp://dev/~name12/+junk/junk.contrib Development ... |
433 | lp://dev/~name12/+junk/junk.dev Experimental ... |
434 | |
435 | @@ -113,7 +112,7 @@ |
436 | >>> links = find_tag_by_id(browser.contents, 'branch-batch-links') |
437 | >>> print links.renderContents() |
438 | <BLANKLINE> |
439 | - ...1...→...6...of 12 results... |
440 | + ...1...→...6...of 11 results... |
441 | |
442 | >>> table = find_tag_by_id(browser.contents, 'branchtable') |
443 | >>> for row in table.tbody.fetch('tr'): |
444 | @@ -129,7 +128,7 @@ |
445 | >>> links = find_tag_by_id(browser.contents, 'branch-batch-links') |
446 | >>> print links.renderContents() |
447 | <BLANKLINE> |
448 | - ...7...→...12...of 12 results... |
449 | + ...7...→...11...of 11 results... |
450 | |
451 | >>> table = find_tag_by_id(browser.contents, 'branchtable') |
452 | >>> for row in table.tbody.fetch('tr'): |
453 | @@ -137,7 +136,8 @@ |
454 | lp://dev/~name12/gnome-terminal/pushed Development ... |
455 | lp://dev/~name12/gnome-terminal/scanned Development ... |
456 | lp://dev/~name12/gnome-terminal/klingon Experimental ... |
457 | - lp://dev/~name12/landscape/feature-x Development ... |
458 | + lp://dev/~name12/+junk/junk.contrib Development ... |
459 | + lp://dev/~name12/+junk/junk.dev Experimental ... |
460 | |
461 | |
462 | Selecting an individual lifecycle status from the select control |
463 | @@ -186,6 +186,7 @@ |
464 | >>> table = find_tag_by_id(browser.contents, 'branchtable') |
465 | >>> for row in table.tbody.fetch('tr'): |
466 | ... print extract_text(row) |
467 | + lp://dev/~name12/firefox/main ... |
468 | lp://dev/~launchpad/gnome-terminal/launchpad ... |
469 | lp://dev/~name12/+junk/junk.dev ... |
470 | |
471 | |
472 | === modified file 'lib/lp/code/stories/feeds/xx-branch-atom.txt' |
473 | --- lib/lp/code/stories/feeds/xx-branch-atom.txt 2011-12-30 06:14:56 +0000 |
474 | +++ lib/lp/code/stories/feeds/xx-branch-atom.txt 2012-07-06 13:00:27 +0000 |
475 | @@ -102,8 +102,7 @@ |
476 | >>> from zope.component import getUtility |
477 | >>> from zope.security.proxy import removeSecurityProxy |
478 | >>> from lp.code.model.branch import Branch |
479 | - >>> from lp.code.interfaces.branchcollection import ( |
480 | - ... IAllBranches) |
481 | + >>> from lp.code.interfaces.branchcollection import IAllBranches |
482 | >>> from lp.registry.interfaces.person import IPersonSet |
483 | >>> login(ANONYMOUS) |
484 | >>> person_set = getUtility(IPersonSet) |
485 | |
486 | === modified file 'lib/lp/code/tests/test_branch_access_policy_triggers.py' |
487 | --- lib/lp/code/tests/test_branch_access_policy_triggers.py 2012-06-15 06:38:35 +0000 |
488 | +++ lib/lp/code/tests/test_branch_access_policy_triggers.py 2012-07-06 13:00:27 +0000 |
489 | @@ -32,9 +32,10 @@ |
490 | |
491 | def test_adding_aag_with_private_branch(self): |
492 | # Adding a new AAG updates the branch columns via trigger. |
493 | + owner = self.factory.makePerson() |
494 | branch = self.factory.makeBranch( |
495 | - information_type=InformationType.USERDATA) |
496 | - self.assertAccess(branch, 65, []) |
497 | + information_type=InformationType.USERDATA, owner=owner) |
498 | + self.assertAccess(branch, 65, [owner.id]) |
499 | artifact = self.factory.makeAccessArtifact(concrete=branch) |
500 | grant = self.factory.makeAccessArtifactGrant(artifact=artifact) |
501 | - self.assertAccess(branch, 65, [grant.grantee.id]) |
502 | + self.assertAccess(branch, 65, [owner.id, grant.grantee.id]) |
503 | |
504 | === modified file 'lib/lp/registry/browser/tests/test_pillar_sharing.py' |
505 | --- lib/lp/registry/browser/tests/test_pillar_sharing.py 2012-06-28 15:48:42 +0000 |
506 | +++ lib/lp/registry/browser/tests/test_pillar_sharing.py 2012-07-06 13:00:27 +0000 |
507 | @@ -78,9 +78,8 @@ |
508 | self.factory.makeAccessPolicyGrant(self.access_policy, grantee) |
509 | return grantee |
510 | |
511 | - def makeArtifactGrantee( |
512 | - self, grantee=None, with_bug=True, |
513 | - with_branch=False, security=False): |
514 | + def makeArtifactGrantee(self, grantee=None, with_bug=True, |
515 | + with_branch=False, security=False): |
516 | if grantee is None: |
517 | grantee = self.factory.makePerson() |
518 | |
519 | @@ -242,7 +241,7 @@ |
520 | IStore(self.pillar).invalidate() |
521 | with StormStatementRecorder() as recorder: |
522 | create_initialized_view(pillarperson, '+index') |
523 | - self.assertThat(recorder, HasQueryCount(LessThan(12))) |
524 | + self.assertThat(recorder, HasQueryCount(LessThan(26))) |
525 | |
526 | def test_view_write_enabled_without_feature_flag(self): |
527 | # Test that sharing_write_enabled is not set without the feature flag. |
528 | |
529 | === modified file 'lib/lp/registry/stories/product/xx-product-development-focus.txt' |
530 | --- lib/lp/registry/stories/product/xx-product-development-focus.txt 2012-06-15 13:56:29 +0000 |
531 | +++ lib/lp/registry/stories/product/xx-product-development-focus.txt 2012-07-06 13:00:27 +0000 |
532 | @@ -146,10 +146,9 @@ |
533 | appears as if there is no series branch set. |
534 | |
535 | >>> login('admin@canonical.com') |
536 | - >>> from zope.security.proxy import removeSecurityProxy |
537 | >>> from lp.registry.enums import InformationType |
538 | - >>> removeSecurityProxy(branch).information_type = ( |
539 | - ... InformationType.USERDATA) |
540 | + >>> branch.transitionToInformationType( |
541 | + ... InformationType.USERDATA, branch.owner, verify_policy=False) |
542 | >>> logout() |
543 | |
544 | >>> anon_browser.open('http://launchpad.dev/fooix') |
545 | |
546 | === modified file 'lib/lp/security.py' |
547 | --- lib/lp/security.py 2012-06-28 01:14:33 +0000 |
548 | +++ lib/lp/security.py 2012-07-06 13:00:27 +0000 |
549 | @@ -2022,8 +2022,8 @@ |
550 | """Controls visibility of branches. |
551 | |
552 | A person can see the branch if the branch is public, they are the owner |
553 | - of the branch, they are in the team that owns the branch, subscribed to |
554 | - the branch, or a launchpad administrator. |
555 | + of the branch, they are in the team that owns the branch, they have an |
556 | + access grant to the branch, or a launchpad administrator. |
557 | """ |
558 | permission = 'launchpad.View' |
559 | usedfor = IBranch |