Merge lp:~bac/launchpad/bug-652149 into lp:launchpad
- bug-652149
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Brad Crittenden |
Approved revision: | 11705 |
Merged at revision: | 11789 |
Proposed branch: | lp:~bac/launchpad/bug-652149 |
Merge into: | lp:launchpad |
Diff against target: |
485 lines (+192/-55) 8 files modified
lib/lp/code/browser/branchlisting.py (+3/-1) lib/lp/code/browser/branchvisibilitypolicy.py (+21/-14) lib/lp/code/browser/tests/test_branchlisting.py (+77/-13) lib/lp/code/stories/branches/xx-branch-visibility-policy.txt (+51/-15) lib/lp/code/templates/branch-visibility.pt (+2/-2) lib/lp/code/templates/project-branches.pt (+37/-8) lib/lp/registry/browser/project.py (+1/-1) lib/lp/testing/sampledata.py (+0/-1) |
To merge this branch: | bzr merge lp:~bac/launchpad/bug-652149 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Curtis Hovey (community) | ui | Approve | |
Henning Eggers (community) | ui* | Approve | |
Jeroen T. Vermeulen (community) | code | Approve | |
Review via email: mp+38705@code.launchpad.net |
Commit message
Provide an indication of the default branch visibility rule for a project group and a link to 'Define branch visibility' for LP admins and commercial admins.
Description of the change
= Summary =
The code view for a project group did not show the default branch rules.
It did have a link for 'Define branch visibility' but the permission on
it was wrong, so commercial admins did not see it.
== Proposed fix ==
Convert the template to main_side, add a portlet for the privacy setting
display and a portlet for the link to define branch visibility.
== Pre-implementation notes ==
Brief chat with Curtis.
== Implementation details ==
I included some drive-by fixes. Removed redundancy from
lp.testing.
product-branches to not use conditional paragraphs.
== Tests ==
bin/test -vvm lp.code -t TestProjectGrou
== Demo and Q/A ==
Visit a project group such as https:/
determine everything is in order.
= Launchpad lint =
Bah, I'll check to ensure the real lint issues are fixed.
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/
1: narrative uses a moin header.
67: source exceeds 78 characters.
94: narrative uses a moin header.
104: source exceeds 78 characters.
107: source exceeds 78 characters.
125: source exceeds 78 characters.
126: want exceeds 78 characters.
134: source exceeds 78 characters.
138: source exceeds 78 characters.
146: narrative uses a moin header.
198: narrative uses a moin header.
231: source exceeds 78 characters.
253: source exceeds 78 characters.
./lib/lp/
1388: E302 expected 2 blank lines, found 1
- 11699. By Brad Crittenden
-
Fixed lint
- 11700. By Brad Crittenden
-
Fixed lint
Brad Crittenden (bac) wrote : | # |
Thanks Jeroen. All of the issues you raised are fixed.
- 11701. By Brad Crittenden
-
Do not use (expensive) browser in tests, refactor BranchVisiblity
PolicyMixin, streamline expected output in tests.
Brad Crittenden (bac) wrote : | # |
Hi Henning,
Could you do a UI review, please? Screenshots available at:
http://
- 11702. By Brad Crittenden
-
Changed display of inherited branch vis policy for projects. Reverted change to product-
branches. pt.
Henning Eggers (henninge) wrote : | # |
Hi Brad,
this looks very good, thank you. The icon misalignment I noticed must be related to other stuff. I see it all over Launchpad now.
I was about to make suggestions about avoiding adding the side portlets and thus losing horizontal space. Horizontal space is a little issue because the project group page has an extra column on the branch listing. The information could be placed in the top portlet but I don't know how long the list of teams might get. Having a long list of team exceptions before reaching the actual branches degrades the usefulness of the page. Maybe the list could have been placed under the branch listing.
But I realized that a project's code page already has that portlet about visibility in the same spot, so I guess this is consistent. IIRC 3.0 UI design removed side portlets from all pages expect project/application home pages?
So the only think I'd like you to think about is if the extra portlet for changing the visibilty. Could that not be integrated into the other portlet by adding an edit icon at the right place?
There is one little nitpick: forbidden-
But there are no real stoppers here, seeing that the use of side portlets seems to be appropriate here. Thank you!
Henning
Curtis Hovey (sinzui) wrote : | # |
I agree with Henning.
Brad Crittenden (bac) wrote : | # |
Thanks for the UI review Henning and Curtis.
I hadn't noticed the extra space at the bottom of the top portlet. I defeated it with a style="
For consistency with the other code branchlisting pages I prefer to keep the "Define branch visibility" link in a second portlet.
- 11703. By Brad Crittenden
-
Fixed portlet spacing
- 11704. By Brad Crittenden
-
Merge from devel. Encountered conflicts with branchlisting files.
- 11705. By Brad Crittenden
-
Merged new tests from devel
Preview Diff
1 | === modified file 'lib/lp/code/browser/branchlisting.py' | |||
2 | --- lib/lp/code/browser/branchlisting.py 2010-10-20 16:04:58 +0000 | |||
3 | +++ lib/lp/code/browser/branchlisting.py 2010-10-23 04:44:50 +0000 | |||
4 | @@ -94,6 +94,7 @@ | |||
5 | 94 | PersonActiveReviewsView, | 94 | PersonActiveReviewsView, |
6 | 95 | PersonProductActiveReviewsView, | 95 | PersonProductActiveReviewsView, |
7 | 96 | ) | 96 | ) |
8 | 97 | from lp.code.browser.branchvisibilitypolicy import BranchVisibilityPolicyMixin | ||
9 | 97 | from lp.code.browser.summary import BranchCountSummaryView | 98 | from lp.code.browser.summary import BranchCountSummaryView |
10 | 98 | from lp.code.enums import ( | 99 | from lp.code.enums import ( |
11 | 99 | BranchLifecycleStatus, | 100 | BranchLifecycleStatus, |
12 | @@ -532,7 +533,8 @@ | |||
13 | 532 | return "listing sortable" | 533 | return "listing sortable" |
14 | 533 | 534 | ||
15 | 534 | 535 | ||
17 | 535 | class BranchListingView(LaunchpadFormView, FeedsMixin): | 536 | class BranchListingView(LaunchpadFormView, FeedsMixin, |
18 | 537 | BranchVisibilityPolicyMixin): | ||
19 | 536 | """A base class for views of branch listings.""" | 538 | """A base class for views of branch listings.""" |
20 | 537 | schema = IBranchListingFilter | 539 | schema = IBranchListingFilter |
21 | 538 | field_names = ['lifecycle', 'sort_by'] | 540 | field_names = ['lifecycle', 'sort_by'] |
22 | 539 | 541 | ||
23 | === modified file 'lib/lp/code/browser/branchvisibilitypolicy.py' | |||
24 | --- lib/lp/code/browser/branchvisibilitypolicy.py 2010-08-31 11:11:09 +0000 | |||
25 | +++ lib/lp/code/browser/branchvisibilitypolicy.py 2010-10-23 04:44:50 +0000 | |||
26 | @@ -8,6 +8,7 @@ | |||
27 | 8 | __all__ = [ | 8 | __all__ = [ |
28 | 9 | 'AddBranchVisibilityTeamPolicyView', | 9 | 'AddBranchVisibilityTeamPolicyView', |
29 | 10 | 'RemoveBranchVisibilityTeamPolicyView', | 10 | 'RemoveBranchVisibilityTeamPolicyView', |
30 | 11 | 'BranchVisibilityPolicyMixin', | ||
31 | 11 | 'BranchVisibilityPolicyView', | 12 | 'BranchVisibilityPolicyView', |
32 | 12 | ] | 13 | ] |
33 | 13 | 14 | ||
34 | @@ -38,6 +39,8 @@ | |||
35 | 38 | BranchVisibilityRule, | 39 | BranchVisibilityRule, |
36 | 39 | TeamBranchVisibilityRule, | 40 | TeamBranchVisibilityRule, |
37 | 40 | ) | 41 | ) |
38 | 42 | from lp.code.interfaces.branchnamespace import IBranchNamespacePolicy | ||
39 | 43 | from lp.code.interfaces.branchtarget import IBranchTarget | ||
40 | 41 | from lp.code.interfaces.branchvisibilitypolicy import ( | 44 | from lp.code.interfaces.branchvisibilitypolicy import ( |
41 | 42 | IBranchVisibilityTeamPolicy, | 45 | IBranchVisibilityTeamPolicy, |
42 | 43 | ) | 46 | ) |
43 | @@ -155,7 +158,24 @@ | |||
44 | 155 | self.context.removeTeamFromBranchVisibilityPolicy(item.team) | 158 | self.context.removeTeamFromBranchVisibilityPolicy(item.team) |
45 | 156 | 159 | ||
46 | 157 | 160 | ||
48 | 158 | class BranchVisibilityPolicyView(LaunchpadView): | 161 | class BranchVisibilityPolicyMixin: |
49 | 162 | """Mixin class providing visibility rules.""" | ||
50 | 163 | @property | ||
51 | 164 | def base_visibility_rule(self): | ||
52 | 165 | return self.context.getBaseBranchVisibilityRule() | ||
53 | 166 | |||
54 | 167 | @property | ||
55 | 168 | def team_policies(self): | ||
56 | 169 | """The policy items that have a valid team.""" | ||
57 | 170 | return [item for item in self.items if item.team is not None] | ||
58 | 171 | |||
59 | 172 | @cachedproperty | ||
60 | 173 | def items(self): | ||
61 | 174 | return self.context.getBranchVisibilityTeamPolicies() | ||
62 | 175 | |||
63 | 176 | |||
64 | 177 | class BranchVisibilityPolicyView(LaunchpadView, | ||
65 | 178 | BranchVisibilityPolicyMixin): | ||
66 | 159 | """Simple view for displaying branch visibility policies.""" | 179 | """Simple view for displaying branch visibility policies.""" |
67 | 160 | 180 | ||
68 | 161 | @property | 181 | @property |
69 | @@ -163,14 +183,6 @@ | |||
70 | 163 | name = self.context.displayname | 183 | name = self.context.displayname |
71 | 164 | return 'Set branch visibility policy for %s' % name | 184 | return 'Set branch visibility policy for %s' % name |
72 | 165 | 185 | ||
73 | 166 | @cachedproperty | ||
74 | 167 | def items(self): | ||
75 | 168 | return self.context.getBranchVisibilityTeamPolicies() | ||
76 | 169 | |||
77 | 170 | @property | ||
78 | 171 | def base_visibility_rule(self): | ||
79 | 172 | return self.context.getBaseBranchVisibilityRule() | ||
80 | 173 | |||
81 | 174 | @property | 186 | @property |
82 | 175 | def can_remove_items(self): | 187 | def can_remove_items(self): |
83 | 176 | """You cannot remove items if using inherited policy or | 188 | """You cannot remove items if using inherited policy or |
84 | @@ -178,8 +190,3 @@ | |||
85 | 178 | """ | 190 | """ |
86 | 179 | return (len(self.items) > 0 and | 191 | return (len(self.items) > 0 and |
87 | 180 | not self.context.isUsingInheritedBranchVisibilityPolicy()) | 192 | not self.context.isUsingInheritedBranchVisibilityPolicy()) |
88 | 181 | |||
89 | 182 | @property | ||
90 | 183 | def team_policies(self): | ||
91 | 184 | """The policy items that have a valid team.""" | ||
92 | 185 | return [item for item in self.items if item.team is not None] | ||
93 | 186 | 193 | ||
94 | === modified file 'lib/lp/code/browser/tests/test_branchlisting.py' | |||
95 | --- lib/lp/code/browser/tests/test_branchlisting.py 2010-10-20 13:53:15 +0000 | |||
96 | +++ lib/lp/code/browser/tests/test_branchlisting.py 2010-10-23 04:44:50 +0000 | |||
97 | @@ -33,11 +33,15 @@ | |||
98 | 33 | GroupedDistributionSourcePackageBranchesView, | 33 | GroupedDistributionSourcePackageBranchesView, |
99 | 34 | SourcePackageBranchesView, | 34 | SourcePackageBranchesView, |
100 | 35 | ) | 35 | ) |
101 | 36 | from lp.code.enums import BranchVisibilityRule | ||
102 | 36 | from lp.code.interfaces.seriessourcepackagebranch import ( | 37 | from lp.code.interfaces.seriessourcepackagebranch import ( |
103 | 37 | IMakeOfficialBranchLinks, | 38 | IMakeOfficialBranchLinks, |
104 | 38 | ) | 39 | ) |
105 | 39 | from lp.code.model.branch import Branch | 40 | from lp.code.model.branch import Branch |
107 | 40 | from lp.registry.interfaces.person import PersonVisibility | 41 | from lp.registry.interfaces.person import ( |
108 | 42 | IPersonSet, | ||
109 | 43 | PersonVisibility, | ||
110 | 44 | ) | ||
111 | 41 | from lp.registry.interfaces.pocket import PackagePublishingPocket | 45 | from lp.registry.interfaces.pocket import PackagePublishingPocket |
112 | 42 | from lp.registry.model.person import Owner | 46 | from lp.registry.model.person import Owner |
113 | 43 | from lp.registry.model.product import Product | 47 | from lp.registry.model.product import Product |
114 | @@ -51,6 +55,10 @@ | |||
115 | 51 | time_counter, | 55 | time_counter, |
116 | 52 | ) | 56 | ) |
117 | 53 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer | 57 | from lp.testing.factory import remove_security_proxy_and_shout_at_engineer |
118 | 58 | from lp.testing.sampledata import ( | ||
119 | 59 | ADMIN_EMAIL, | ||
120 | 60 | COMMERCIAL_ADMIN_EMAIL, | ||
121 | 61 | ) | ||
122 | 54 | from lp.testing.views import create_initialized_view | 62 | from lp.testing.views import create_initialized_view |
123 | 55 | 63 | ||
124 | 56 | 64 | ||
125 | @@ -422,35 +430,91 @@ | |||
126 | 422 | self.assertIs(None, branches) | 430 | self.assertIs(None, branches) |
127 | 423 | 431 | ||
128 | 424 | 432 | ||
130 | 425 | class TestProjectBranchListing(TestCaseWithFactory): | 433 | class TestProjectGroupBranches(TestCaseWithFactory): |
131 | 434 | """Test for the project group branches page.""" | ||
132 | 426 | 435 | ||
133 | 427 | layer = DatabaseFunctionalLayer | 436 | layer = DatabaseFunctionalLayer |
134 | 428 | 437 | ||
135 | 429 | def setUp(self): | 438 | def setUp(self): |
137 | 430 | super(TestProjectBranchListing, self).setUp() | 439 | TestCaseWithFactory.setUp(self) |
138 | 431 | self.project = self.factory.makeProject() | 440 | self.project = self.factory.makeProject() |
140 | 432 | self.product = self.factory.makeProduct(project=self.project) | 441 | |
141 | 442 | def test_project_with_no_branch_visibility_rule(self): | ||
142 | 443 | view = create_initialized_view( | ||
143 | 444 | self.project, name="+branches", rootsite='code') | ||
144 | 445 | privacy_portlet = find_tag_by_id(view(), 'privacy') | ||
145 | 446 | text = extract_text(privacy_portlet) | ||
146 | 447 | expected = """ | ||
147 | 448 | Inherited branch visibility for all projects in .* is Public. | ||
148 | 449 | """ | ||
149 | 450 | self.assertTextMatchesExpressionIgnoreWhitespace( | ||
150 | 451 | expected, text) | ||
151 | 452 | |||
152 | 453 | def test_project_with_private_branch_visibility_rule(self): | ||
153 | 454 | self.project.setBranchVisibilityTeamPolicy( | ||
154 | 455 | None, BranchVisibilityRule.FORBIDDEN) | ||
155 | 456 | view = create_initialized_view( | ||
156 | 457 | self.project, name="+branches", rootsite='code') | ||
157 | 458 | privacy_portlet = find_tag_by_id(view(), 'privacy') | ||
158 | 459 | text = extract_text(privacy_portlet) | ||
159 | 460 | expected = """ | ||
160 | 461 | Inherited branch visibility for all projects in .* is Forbidden. | ||
161 | 462 | """ | ||
162 | 463 | self.assertTextMatchesExpressionIgnoreWhitespace( | ||
163 | 464 | expected, text) | ||
164 | 465 | |||
165 | 466 | def _testBranchVisibilityLink(self, user): | ||
166 | 467 | login_person(user) | ||
167 | 468 | view = create_initialized_view( | ||
168 | 469 | self.project, name="+branches", rootsite='code', | ||
169 | 470 | principal=user) | ||
170 | 471 | action_portlet = find_tag_by_id(view(), 'action-portlet') | ||
171 | 472 | text = extract_text(action_portlet) | ||
172 | 473 | expected = '.*Define branch visibility.*' | ||
173 | 474 | self.assertTextMatchesExpressionIgnoreWhitespace( | ||
174 | 475 | expected, text) | ||
175 | 476 | |||
176 | 477 | def test_branch_visibility_link_admin(self): | ||
177 | 478 | # An admin will be displayed a link to define branch visibility in the | ||
178 | 479 | # action portlet. | ||
179 | 480 | admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL) | ||
180 | 481 | self._testBranchVisibilityLink(admin) | ||
181 | 482 | |||
182 | 483 | def test_branch_visibility_link_commercial_admin(self): | ||
183 | 484 | # A commercial admin will be displayed a link to define branch | ||
184 | 485 | # visibility in the action portlet. | ||
185 | 486 | admin = getUtility(IPersonSet).getByEmail(COMMERCIAL_ADMIN_EMAIL) | ||
186 | 487 | self._testBranchVisibilityLink(admin) | ||
187 | 488 | |||
188 | 489 | def test_branch_visibility_link_non_admin(self): | ||
189 | 490 | # A non-admin will not see the action portlet. | ||
190 | 491 | view = create_initialized_view( | ||
191 | 492 | self.project, name="+branches", rootsite='code') | ||
192 | 493 | action_portlet = find_tag_by_id(view(), 'action-portlet') | ||
193 | 494 | self.assertIs(None, action_portlet) | ||
194 | 433 | 495 | ||
195 | 434 | def test_no_branches_gets_message_not_listing(self): | 496 | def test_no_branches_gets_message_not_listing(self): |
196 | 435 | # If there are no product branches on the project's products, then | 497 | # If there are no product branches on the project's products, then |
197 | 436 | # the view shows the no code hosting message instead of a listing. | 498 | # the view shows the no code hosting message instead of a listing. |
200 | 437 | browser = self.getUserBrowser( | 499 | self.factory.makeProduct(project=self.project) |
201 | 438 | canonical_url(self.project, rootsite='code')) | 500 | view = create_initialized_view( |
202 | 501 | self.project, name='+branches', rootsite='code') | ||
203 | 439 | displayname = self.project.displayname | 502 | displayname = self.project.displayname |
204 | 440 | expected_text = normalize_whitespace( | 503 | expected_text = normalize_whitespace( |
208 | 441 | ("Launchpad does not know where any of %s's " | 504 | ("Launchpad does not know where any of %s's " |
209 | 442 | "projects host their code." % displayname)) | 505 | "projects host their code." % displayname)) |
210 | 443 | no_branch_div = find_tag_by_id(browser.contents, "no-branchtable") | 506 | no_branch_div = find_tag_by_id(view(), "no-branchtable") |
211 | 444 | text = normalize_whitespace(extract_text(no_branch_div)) | 507 | text = normalize_whitespace(extract_text(no_branch_div)) |
212 | 445 | self.assertEqual(expected_text, text) | 508 | self.assertEqual(expected_text, text) |
213 | 446 | 509 | ||
214 | 447 | def test_branches_get_listing(self): | 510 | def test_branches_get_listing(self): |
215 | 448 | # If a product has a branch, then the project view has a branch | 511 | # If a product has a branch, then the project view has a branch |
216 | 449 | # listing. | 512 | # listing. |
221 | 450 | branch = self.factory.makeProductBranch(product=self.product) | 513 | product = self.factory.makeProduct(project=self.project) |
222 | 451 | browser = self.getUserBrowser( | 514 | self.factory.makeProductBranch(product=product) |
223 | 452 | canonical_url(self.project, rootsite='code')) | 515 | view = create_initialized_view( |
224 | 453 | table = find_tag_by_id(browser.contents, "branchtable") | 516 | self.project, name='+branches', rootsite='code') |
225 | 517 | table = find_tag_by_id(view(), "branchtable") | ||
226 | 454 | self.assertIsNot(None, table) | 518 | self.assertIsNot(None, table) |
227 | 455 | 519 | ||
228 | 456 | 520 | ||
229 | 457 | 521 | ||
230 | === modified file 'lib/lp/code/stories/branches/xx-branch-visibility-policy.txt' | |||
231 | --- lib/lp/code/stories/branches/xx-branch-visibility-policy.txt 2009-07-20 18:22:54 +0000 | |||
232 | +++ lib/lp/code/stories/branches/xx-branch-visibility-policy.txt 2010-10-23 04:44:50 +0000 | |||
233 | @@ -1,7 +1,8 @@ | |||
235 | 1 | = Branch Visibility Policy Pages = | 1 | Branch Visibility Policy Pages |
236 | 2 | ============================== | ||
237 | 2 | 3 | ||
238 | 3 | Controlling the branch visibility policies for products and projects is only | 4 | Controlling the branch visibility policies for products and projects is only |
240 | 4 | available to launchpad admins and launchpad commercial admins. | 5 | available to Launchpad admins and Launchpad commercial admins. |
241 | 5 | 6 | ||
242 | 6 | Not to anonymous people. | 7 | Not to anonymous people. |
243 | 7 | 8 | ||
244 | @@ -64,12 +65,36 @@ | |||
245 | 64 | >>> print commercial_browser.url | 65 | >>> print commercial_browser.url |
246 | 65 | http://launchpad.dev/firefox/+branchvisibility | 66 | http://launchpad.dev/firefox/+branchvisibility |
247 | 66 | 67 | ||
249 | 67 | >>> commercial_browser.getLink('Customise policy for Mozilla Firefox').click() | 68 | >>> commercial_browser.getLink( |
250 | 69 | ... 'Customise policy for Mozilla Firefox').click() | ||
251 | 68 | >>> print commercial_browser.url | 70 | >>> print commercial_browser.url |
252 | 69 | http://launchpad.dev/firefox/+addbranchvisibilitypolicy | 71 | http://launchpad.dev/firefox/+addbranchvisibilitypolicy |
253 | 70 | 72 | ||
256 | 71 | 73 | Admins can define branch visibility on projects, too. | |
257 | 72 | == Default policies == | 74 | |
258 | 75 | >>> admin_browser.open('http://code.launchpad.dev/mozilla') | ||
259 | 76 | >>> admin_browser.getLink('Define branch visibility').click() | ||
260 | 77 | >>> print admin_browser.url | ||
261 | 78 | http://launchpad.dev/mozilla/+branchvisibility | ||
262 | 79 | |||
263 | 80 | >>> admin_browser.getLink('Set policy for a team').click() | ||
264 | 81 | >>> print admin_browser.url | ||
265 | 82 | http://launchpad.dev/mozilla/+addbranchvisibilitypolicy | ||
266 | 83 | |||
267 | 84 | As can commercial admins. | ||
268 | 85 | |||
269 | 86 | >>> commercial_browser.open('http://code.launchpad.dev/mozilla') | ||
270 | 87 | >>> commercial_browser.getLink('Define branch visibility').click() | ||
271 | 88 | >>> print commercial_browser.url | ||
272 | 89 | http://launchpad.dev/mozilla/+branchvisibility | ||
273 | 90 | |||
274 | 91 | >>> commercial_browser.getLink('Set policy for a team').click() | ||
275 | 92 | >>> print commercial_browser.url | ||
276 | 93 | http://launchpad.dev/mozilla/+addbranchvisibilitypolicy | ||
277 | 94 | |||
278 | 95 | |||
279 | 96 | Default policies | ||
280 | 97 | ---------------- | ||
281 | 73 | 98 | ||
282 | 74 | The default policies are to have all branches public. When the branch policy | 99 | The default policies are to have all branches public. When the branch policy |
283 | 75 | objects are created for products they are constructed with the branch policy | 100 | objects are created for products they are constructed with the branch policy |
284 | @@ -79,10 +104,12 @@ | |||
285 | 79 | 104 | ||
286 | 80 | >>> admin_browser.open('http://launchpad.dev/firefox/+branchvisibility') | 105 | >>> admin_browser.open('http://launchpad.dev/firefox/+branchvisibility') |
287 | 81 | 106 | ||
289 | 82 | >>> print extract_text(find_tag_by_id(admin_browser.contents, 'inherited')) | 107 | >>> print extract_text( |
290 | 108 | ... find_tag_by_id(admin_browser.contents, 'inherited')) | ||
291 | 83 | Using inherited policy from the Mozilla Project. | 109 | Using inherited policy from the Mozilla Project. |
292 | 84 | 110 | ||
294 | 85 | >>> print extract_text(find_tag_by_id(admin_browser.contents, 'default-policy')) | 111 | >>> print extract_text( |
295 | 112 | ... find_tag_by_id(admin_browser.contents, 'default-policy')) | ||
296 | 86 | Default branch visibility for all branches in Mozilla Firefox is Public. | 113 | Default branch visibility for all branches in Mozilla Firefox is Public. |
297 | 87 | 114 | ||
298 | 88 | When the project is using the inherited policy, the user can either | 115 | When the project is using the inherited policy, the user can either |
299 | @@ -100,8 +127,11 @@ | |||
300 | 100 | >>> admin_browser.getLink('Edit inherited policy').click() | 127 | >>> admin_browser.getLink('Edit inherited policy').click() |
301 | 101 | >>> print find_tag_by_id(admin_browser.contents, 'inherited') | 128 | >>> print find_tag_by_id(admin_browser.contents, 'inherited') |
302 | 102 | None | 129 | None |
305 | 103 | >>> print extract_text(find_tag_by_id(admin_browser.contents, 'default-policy')) | 130 | >>> print extract_text( |
306 | 104 | Default branch visibility for all branches in the Mozilla Project is Public. | 131 | ... find_tag_by_id(admin_browser.contents, 'default-policy')) |
307 | 132 | Default branch visibility for all branches | ||
308 | 133 | in the Mozilla Project is Public. | ||
309 | 134 | |||
310 | 105 | >>> actions = find_tag_by_id(admin_browser.contents, 'policy-actions') | 135 | >>> actions = find_tag_by_id(admin_browser.contents, 'policy-actions') |
311 | 106 | >>> for anchor in actions.fetch('a'): | 136 | >>> for anchor in actions.fetch('a'): |
312 | 107 | ... print '%s -> %s' % (anchor.renderContents(), anchor['href']) | 137 | ... print '%s -> %s' % (anchor.renderContents(), anchor['href']) |
313 | @@ -109,11 +139,13 @@ | |||
314 | 109 | 139 | ||
315 | 110 | Products that don't have an associated project look similar to projects. | 140 | Products that don't have an associated project look similar to projects. |
316 | 111 | 141 | ||
318 | 112 | >>> admin_browser.open('http://launchpad.dev/alsa-utils/+branchvisibility') | 142 | >>> admin_browser.open( |
319 | 143 | ... 'http://launchpad.dev/alsa-utils/+branchvisibility') | ||
320 | 113 | 144 | ||
321 | 114 | >>> print find_tag_by_id(admin_browser.contents, 'inherited') | 145 | >>> print find_tag_by_id(admin_browser.contents, 'inherited') |
322 | 115 | None | 146 | None |
324 | 116 | >>> print extract_text(find_tag_by_id(admin_browser.contents, 'default-policy')) | 147 | >>> print extract_text( |
325 | 148 | ... find_tag_by_id(admin_browser.contents, 'default-policy')) | ||
326 | 117 | Default branch visibility for all branches in alsa-utils is Public. | 149 | Default branch visibility for all branches in alsa-utils is Public. |
327 | 118 | >>> actions = find_tag_by_id(admin_browser.contents, 'policy-actions') | 150 | >>> actions = find_tag_by_id(admin_browser.contents, 'policy-actions') |
328 | 119 | >>> for anchor in actions.fetch('a'): | 151 | >>> for anchor in actions.fetch('a'): |
329 | @@ -121,7 +153,8 @@ | |||
330 | 121 | Set policy for a team -> +addbranchvisibilitypolicy | 153 | Set policy for a team -> +addbranchvisibilitypolicy |
331 | 122 | 154 | ||
332 | 123 | 155 | ||
334 | 124 | == Overriding the inherited policy == | 156 | Overriding the inherited policy |
335 | 157 | ------------------------------- | ||
336 | 125 | 158 | ||
337 | 126 | Setting any policy item overrides the use of an inherited policy, even if | 159 | Setting any policy item overrides the use of an inherited policy, even if |
338 | 127 | it new policy item just specifies public branches for everyone. | 160 | it new policy item just specifies public branches for everyone. |
339 | @@ -173,7 +206,8 @@ | |||
340 | 173 | Ubuntu Gnome Team: Private | 206 | Ubuntu Gnome Team: Private |
341 | 174 | 207 | ||
342 | 175 | 208 | ||
344 | 176 | == Removing policy items == | 209 | Removing policy items |
345 | 210 | --------------------- | ||
346 | 177 | 211 | ||
347 | 178 | When removing the policy items, the defined items are shown as a list | 212 | When removing the policy items, the defined items are shown as a list |
348 | 179 | of checkboxes. Any number of these can be selected, and when the | 213 | of checkboxes. Any number of these can be selected, and when the |
349 | @@ -206,7 +240,8 @@ | |||
350 | 206 | Before we remove them, let's ensure that the commercial admins can see | 240 | Before we remove them, let's ensure that the commercial admins can see |
351 | 207 | the removal page. | 241 | the removal page. |
352 | 208 | 242 | ||
354 | 209 | >>> commercial_browser.open('http://launchpad.dev/firefox/+branchvisibility') | 243 | >>> commercial_browser.open( |
355 | 244 | ... 'http://launchpad.dev/firefox/+branchvisibility') | ||
356 | 210 | >>> commercial_browser.getLink('Remove policy items').click() | 245 | >>> commercial_browser.getLink('Remove policy items').click() |
357 | 211 | >>> print commercial_browser.url | 246 | >>> print commercial_browser.url |
358 | 212 | http://launchpad.dev/firefox/+removebranchvisibilitypolicy | 247 | http://launchpad.dev/firefox/+removebranchvisibilitypolicy |
359 | @@ -228,7 +263,8 @@ | |||
360 | 228 | Firefox will go back to inheriting the polices of Mozilla. Let's let | 263 | Firefox will go back to inheriting the polices of Mozilla. Let's let |
361 | 229 | the commercial admin do the removal to ensure he has the permission. | 264 | the commercial admin do the removal to ensure he has the permission. |
362 | 230 | 265 | ||
364 | 231 | >>> commercial_browser.open('http://launchpad.dev/firefox/+branchvisibility') | 266 | >>> commercial_browser.open( |
365 | 267 | ... 'http://launchpad.dev/firefox/+branchvisibility') | ||
366 | 232 | >>> commercial_browser.getLink('Remove policy items').click() | 268 | >>> commercial_browser.getLink('Remove policy items').click() |
367 | 233 | >>> commercial_browser.getControl('Ubuntu Gnome Team: Private').click() | 269 | >>> commercial_browser.getControl('Ubuntu Gnome Team: Private').click() |
368 | 234 | >>> commercial_browser.getControl('Remove Selected Policy Items').click() | 270 | >>> commercial_browser.getControl('Remove Selected Policy Items').click() |
369 | 235 | 271 | ||
370 | === modified file 'lib/lp/code/templates/branch-visibility.pt' | |||
371 | --- lib/lp/code/templates/branch-visibility.pt 2009-08-24 02:09:05 +0000 | |||
372 | +++ lib/lp/code/templates/branch-visibility.pt 2010-10-23 04:44:50 +0000 | |||
373 | @@ -41,7 +41,7 @@ | |||
374 | 41 | </div> | 41 | </div> |
375 | 42 | 42 | ||
376 | 43 | <div style="padding-left: 1em" id="policy-actions"> | 43 | <div style="padding-left: 1em" id="policy-actions"> |
378 | 44 | <tal:using-inhertied-policy condition="context/isUsingInheritedBranchVisibilityPolicy"> | 44 | <tal:using-inherited-policy condition="context/isUsingInheritedBranchVisibilityPolicy"> |
379 | 45 | <p> | 45 | <p> |
380 | 46 | <img src="/@@/edit" alt="edit" /> | 46 | <img src="/@@/edit" alt="edit" /> |
381 | 47 | <a tal:define="inherited_url context/project/fmt:url" | 47 | <a tal:define="inherited_url context/project/fmt:url" |
382 | @@ -56,7 +56,7 @@ | |||
383 | 56 | </tal:displayname> | 56 | </tal:displayname> |
384 | 57 | </a> | 57 | </a> |
385 | 58 | </p> | 58 | </p> |
387 | 59 | </tal:using-inhertied-policy> | 59 | </tal:using-inherited-policy> |
388 | 60 | 60 | ||
389 | 61 | <tal:no-inhertied-policy condition="not: context/isUsingInheritedBranchVisibilityPolicy"> | 61 | <tal:no-inhertied-policy condition="not: context/isUsingInheritedBranchVisibilityPolicy"> |
390 | 62 | <p> | 62 | <p> |
391 | 63 | 63 | ||
392 | === modified file 'lib/lp/code/templates/project-branches.pt' | |||
393 | --- lib/lp/code/templates/project-branches.pt 2010-10-18 21:32:32 +0000 | |||
394 | +++ lib/lp/code/templates/project-branches.pt 2010-10-23 04:44:50 +0000 | |||
395 | @@ -3,20 +3,49 @@ | |||
396 | 3 | xmlns:tal="http://xml.zope.org/namespaces/tal" | 3 | xmlns:tal="http://xml.zope.org/namespaces/tal" |
397 | 4 | xmlns:metal="http://xml.zope.org/namespaces/metal" | 4 | xmlns:metal="http://xml.zope.org/namespaces/metal" |
398 | 5 | xmlns:i18n="http://xml.zope.org/namespaces/i18n" | 5 | xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
400 | 6 | metal:use-macro="view/macro:page/main_only" | 6 | metal:use-macro="view/macro:page/main_side" |
401 | 7 | i18n:domain="launchpad"> | 7 | i18n:domain="launchpad"> |
402 | 8 | 8 | ||
403 | 9 | <body> | 9 | <body> |
404 | 10 | 10 | ||
405 | 11 | <metal:side fill-slot="side" | ||
406 | 12 | tal:define="context_menu context/menu:context" | ||
407 | 13 | tal:condition="not: | ||
408 | 14 | context/codehosting_usage/enumvalue:UNKNOWN"> | ||
409 | 15 | <div id="privacy" | ||
410 | 16 | tal:define="priv not:view/base_visibility_rule/enumvalue:PUBLIC" | ||
411 | 17 | tal:attributes="class python: priv and 'first portlet private' or 'first portlet public'"> | ||
412 | 18 | <p id="default-policy" style="margin-bottom: 0;"> | ||
413 | 19 | Inherited branch visibility for all projects in | ||
414 | 20 | <strong tal:content="context/displayname">Project</strong> is | ||
415 | 21 | <strong tal:content="view/base_visibility_rule/title">Public</strong>. | ||
416 | 22 | </p> | ||
417 | 23 | |||
418 | 24 | <tal:has-policies condition="view/team_policies"> | ||
419 | 25 | <p>Except for the following teams:</p> | ||
420 | 26 | <ul id="team-policies"> | ||
421 | 27 | <li tal:repeat="item view/team_policies"> | ||
422 | 28 | <tal:team replace="structure item/team/fmt:link:mainsite" | ||
423 | 29 | condition="item/team">Team Name</tal:team>: | ||
424 | 30 | <tal:team condition="not: item/team">Everyone</tal:team> | ||
425 | 31 | <tal:policy replace="item/rule/title">Public</tal:policy> | ||
426 | 32 | </li> | ||
427 | 33 | </ul> | ||
428 | 34 | </tal:has-policies> | ||
429 | 35 | </div> | ||
430 | 36 | |||
431 | 37 | <div id="action-portlet" | ||
432 | 38 | class="portlet" | ||
433 | 39 | tal:define="menu context/menu:overview; | ||
434 | 40 | link menu/branch_visibility" | ||
435 | 41 | tal:condition="link/enabled"> | ||
436 | 42 | <div tal:content="structure link/render" /> | ||
437 | 43 | </div> | ||
438 | 44 | </metal:side> | ||
439 | 45 | |||
440 | 11 | <div metal:fill-slot="main" | 46 | <div metal:fill-slot="main" |
441 | 12 | tal:define="branches view/branches"> | 47 | tal:define="branches view/branches"> |
442 | 13 | 48 | ||
443 | 14 | <div style="float:right" id="floating-links" | ||
444 | 15 | tal:define="menu context/menu:overview"> | ||
445 | 16 | <div tal:define="link menu/branch_visibility" | ||
446 | 17 | tal:condition="link/enabled" | ||
447 | 18 | tal:content="structure link/render" /> | ||
448 | 19 | </div> | ||
449 | 20 | <tal:no-branches | 49 | <tal:no-branches |
450 | 21 | condition="not:context/has_branches"> | 50 | condition="not:context/has_branches"> |
451 | 22 | <div id="no-branchtable"> | 51 | <div id="no-branchtable"> |
452 | @@ -35,7 +64,7 @@ | |||
453 | 35 | <li> | 64 | <li> |
454 | 36 | <a tal:attributes="href product/@@+code-index/configure_codehosting/fmt:url" | 65 | <a tal:attributes="href product/@@+code-index/configure_codehosting/fmt:url" |
455 | 37 | tal:content="product/title" /> | 66 | tal:content="product/title" /> |
457 | 38 | </li> | 67 | </li> |
458 | 39 | </ul> | 68 | </ul> |
459 | 40 | </div> | 69 | </div> |
460 | 41 | </div> | 70 | </div> |
461 | 42 | 71 | ||
462 | === modified file 'lib/lp/registry/browser/project.py' | |||
463 | --- lib/lp/registry/browser/project.py 2010-09-23 03:17:10 +0000 | |||
464 | +++ lib/lp/registry/browser/project.py 2010-10-23 04:44:50 +0000 | |||
465 | @@ -254,7 +254,7 @@ | |||
466 | 254 | 'RDF</abbr> metadata') | 254 | 'RDF</abbr> metadata') |
467 | 255 | return Link('+rdf', text, icon='download-icon') | 255 | return Link('+rdf', text, icon='download-icon') |
468 | 256 | 256 | ||
470 | 257 | @enabled_with_permission('launchpad.Admin') | 257 | @enabled_with_permission('launchpad.Commercial') |
471 | 258 | def branch_visibility(self): | 258 | def branch_visibility(self): |
472 | 259 | text = 'Define branch visibility' | 259 | text = 'Define branch visibility' |
473 | 260 | return Link('+branchvisibility', text, icon='edit', site='mainsite') | 260 | return Link('+branchvisibility', text, icon='edit', site='mainsite') |
474 | 261 | 261 | ||
475 | === modified file 'lib/lp/testing/sampledata.py' | |||
476 | --- lib/lp/testing/sampledata.py 2010-08-30 17:05:49 +0000 | |||
477 | +++ lib/lp/testing/sampledata.py 2010-10-23 04:44:50 +0000 | |||
478 | @@ -58,7 +58,6 @@ | |||
479 | 58 | USER_EMAIL = 'test@canonical.com' | 58 | USER_EMAIL = 'test@canonical.com' |
480 | 59 | VCS_IMPORTS_MEMBER_EMAIL = 'david.allouche@canonical.com' | 59 | VCS_IMPORTS_MEMBER_EMAIL = 'david.allouche@canonical.com' |
481 | 60 | COMMERCIAL_ADMIN_EMAIL = 'commercial-member@canonical.com' | 60 | COMMERCIAL_ADMIN_EMAIL = 'commercial-member@canonical.com' |
482 | 61 | ADMIN_EMAIL = 'foo.bar@canonical.com' | ||
483 | 62 | SAMPLE_PERSON_EMAIL = USER_EMAIL | 61 | SAMPLE_PERSON_EMAIL = USER_EMAIL |
484 | 63 | # A user that is an admin of ubuntu-team, which has upload rights | 62 | # A user that is an admin of ubuntu-team, which has upload rights |
485 | 64 | # to Ubuntu. | 63 | # to Ubuntu. |
Looks fine, apart from a few things we discussed on IRC: view())
* Render & search views in your tests using BeautifulSoup(
* Multi-line strings would be nicer than ("line" "line") in tests.
* The view inherits from both LaunchpadFormView and, indirectly, LaunchpadView.