Merge lp:~bac/launchpad/bug-652149 into lp:launchpad

Proposed by Brad Crittenden
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
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.sampledata and cleaned up the page template for
product-branches to not use conditional paragraphs.

== Tests ==

bin/test -vvm lp.code -t TestProjectGroupBranchesPage

== Demo and Q/A ==

Visit a project group such as https://launchpad.dev/mozilla and
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/code/templates/project-branches.pt
  lib/lp/code/templates/product-branches.pt
  lib/lp/code/browser/tests/test_branchlisting.py
  lib/lp/testing/sampledata.py
  lib/lp/code/stories/branches/xx-branch-visibility-policy.txt
  lib/lp/registry/browser/project.py
  lib/lp/code/browser/branchlisting.py

./lib/lp/code/stories/branches/xx-branch-visibility-policy.txt
       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/code/browser/branchlisting.py
    1388: E302 expected 2 blank lines, found 1

To post a comment you must log in.
lp:~bac/launchpad/bug-652149 updated
11699. By Brad Crittenden

Fixed lint

11700. By Brad Crittenden

Fixed lint

Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

Looks fine, apart from a few things we discussed on IRC:
 * Render & search views in your tests using BeautifulSoup(view())
 * Multi-line strings would be nicer than ("line" "line") in tests.
 * The view inherits from both LaunchpadFormView and, indirectly, LaunchpadView.

review: Approve (code)
Revision history for this message
Brad Crittenden (bac) wrote :

Thanks Jeroen. All of the issues you raised are fixed.

lp:~bac/launchpad/bug-652149 updated
11701. By Brad Crittenden

Do not use (expensive) browser in tests, refactor BranchVisiblityPolicyMixin, streamline expected output in tests.

Revision history for this message
Brad Crittenden (bac) wrote :

Hi Henning,

Could you do a UI review, please? Screenshots available at:
http://people.canonical.com/~bac/branchvis/

lp:~bac/launchpad/bug-652149 updated
11702. By Brad Crittenden

Changed display of inherited branch vis policy for projects. Reverted change to product-branches.pt.

Revision history for this message
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-no-teams.png shows some whitespace at the bottom of the upper portlet. This appears on other similar portlets, too, and is owed to the text being in a <p> tag which has margin-bottom set to 0.3em or so. Could that be avoided somehow?

But there are no real stoppers here, seeing that the use of side portlets seems to be appropriate here. Thank you!

Henning

review: Approve (ui*)
Revision history for this message
Curtis Hovey (sinzui) wrote :

I agree with Henning.

review: Approve (ui)
Revision history for this message
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="margin-bottom: 0;" and it looks nicer.

For consistency with the other code branchlisting pages I prefer to keep the "Define branch visibility" link in a second portlet.

lp:~bac/launchpad/bug-652149 updated
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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 PersonActiveReviewsView,
6 PersonProductActiveReviewsView,
7 )
8+from lp.code.browser.branchvisibilitypolicy import BranchVisibilityPolicyMixin
9 from lp.code.browser.summary import BranchCountSummaryView
10 from lp.code.enums import (
11 BranchLifecycleStatus,
12@@ -532,7 +533,8 @@
13 return "listing sortable"
14
15
16-class BranchListingView(LaunchpadFormView, FeedsMixin):
17+class BranchListingView(LaunchpadFormView, FeedsMixin,
18+ BranchVisibilityPolicyMixin):
19 """A base class for views of branch listings."""
20 schema = IBranchListingFilter
21 field_names = ['lifecycle', 'sort_by']
22
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 __all__ = [
28 'AddBranchVisibilityTeamPolicyView',
29 'RemoveBranchVisibilityTeamPolicyView',
30+ 'BranchVisibilityPolicyMixin',
31 'BranchVisibilityPolicyView',
32 ]
33
34@@ -38,6 +39,8 @@
35 BranchVisibilityRule,
36 TeamBranchVisibilityRule,
37 )
38+from lp.code.interfaces.branchnamespace import IBranchNamespacePolicy
39+from lp.code.interfaces.branchtarget import IBranchTarget
40 from lp.code.interfaces.branchvisibilitypolicy import (
41 IBranchVisibilityTeamPolicy,
42 )
43@@ -155,7 +158,24 @@
44 self.context.removeTeamFromBranchVisibilityPolicy(item.team)
45
46
47-class BranchVisibilityPolicyView(LaunchpadView):
48+class BranchVisibilityPolicyMixin:
49+ """Mixin class providing visibility rules."""
50+ @property
51+ def base_visibility_rule(self):
52+ return self.context.getBaseBranchVisibilityRule()
53+
54+ @property
55+ def team_policies(self):
56+ """The policy items that have a valid team."""
57+ return [item for item in self.items if item.team is not None]
58+
59+ @cachedproperty
60+ def items(self):
61+ return self.context.getBranchVisibilityTeamPolicies()
62+
63+
64+class BranchVisibilityPolicyView(LaunchpadView,
65+ BranchVisibilityPolicyMixin):
66 """Simple view for displaying branch visibility policies."""
67
68 @property
69@@ -163,14 +183,6 @@
70 name = self.context.displayname
71 return 'Set branch visibility policy for %s' % name
72
73- @cachedproperty
74- def items(self):
75- return self.context.getBranchVisibilityTeamPolicies()
76-
77- @property
78- def base_visibility_rule(self):
79- return self.context.getBaseBranchVisibilityRule()
80-
81 @property
82 def can_remove_items(self):
83 """You cannot remove items if using inherited policy or
84@@ -178,8 +190,3 @@
85 """
86 return (len(self.items) > 0 and
87 not self.context.isUsingInheritedBranchVisibilityPolicy())
88-
89- @property
90- def team_policies(self):
91- """The policy items that have a valid team."""
92- return [item for item in self.items if item.team is not None]
93
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 GroupedDistributionSourcePackageBranchesView,
99 SourcePackageBranchesView,
100 )
101+from lp.code.enums import BranchVisibilityRule
102 from lp.code.interfaces.seriessourcepackagebranch import (
103 IMakeOfficialBranchLinks,
104 )
105 from lp.code.model.branch import Branch
106-from lp.registry.interfaces.person import PersonVisibility
107+from lp.registry.interfaces.person import (
108+ IPersonSet,
109+ PersonVisibility,
110+ )
111 from lp.registry.interfaces.pocket import PackagePublishingPocket
112 from lp.registry.model.person import Owner
113 from lp.registry.model.product import Product
114@@ -51,6 +55,10 @@
115 time_counter,
116 )
117 from lp.testing.factory import remove_security_proxy_and_shout_at_engineer
118+from lp.testing.sampledata import (
119+ ADMIN_EMAIL,
120+ COMMERCIAL_ADMIN_EMAIL,
121+ )
122 from lp.testing.views import create_initialized_view
123
124
125@@ -422,35 +430,91 @@
126 self.assertIs(None, branches)
127
128
129-class TestProjectBranchListing(TestCaseWithFactory):
130+class TestProjectGroupBranches(TestCaseWithFactory):
131+ """Test for the project group branches page."""
132
133 layer = DatabaseFunctionalLayer
134
135 def setUp(self):
136- super(TestProjectBranchListing, self).setUp()
137+ TestCaseWithFactory.setUp(self)
138 self.project = self.factory.makeProject()
139- self.product = self.factory.makeProduct(project=self.project)
140+
141+ def test_project_with_no_branch_visibility_rule(self):
142+ view = create_initialized_view(
143+ self.project, name="+branches", rootsite='code')
144+ privacy_portlet = find_tag_by_id(view(), 'privacy')
145+ text = extract_text(privacy_portlet)
146+ expected = """
147+ Inherited branch visibility for all projects in .* is Public.
148+ """
149+ self.assertTextMatchesExpressionIgnoreWhitespace(
150+ expected, text)
151+
152+ def test_project_with_private_branch_visibility_rule(self):
153+ self.project.setBranchVisibilityTeamPolicy(
154+ None, BranchVisibilityRule.FORBIDDEN)
155+ view = create_initialized_view(
156+ self.project, name="+branches", rootsite='code')
157+ privacy_portlet = find_tag_by_id(view(), 'privacy')
158+ text = extract_text(privacy_portlet)
159+ expected = """
160+ Inherited branch visibility for all projects in .* is Forbidden.
161+ """
162+ self.assertTextMatchesExpressionIgnoreWhitespace(
163+ expected, text)
164+
165+ def _testBranchVisibilityLink(self, user):
166+ login_person(user)
167+ view = create_initialized_view(
168+ self.project, name="+branches", rootsite='code',
169+ principal=user)
170+ action_portlet = find_tag_by_id(view(), 'action-portlet')
171+ text = extract_text(action_portlet)
172+ expected = '.*Define branch visibility.*'
173+ self.assertTextMatchesExpressionIgnoreWhitespace(
174+ expected, text)
175+
176+ def test_branch_visibility_link_admin(self):
177+ # An admin will be displayed a link to define branch visibility in the
178+ # action portlet.
179+ admin = getUtility(IPersonSet).getByEmail(ADMIN_EMAIL)
180+ self._testBranchVisibilityLink(admin)
181+
182+ def test_branch_visibility_link_commercial_admin(self):
183+ # A commercial admin will be displayed a link to define branch
184+ # visibility in the action portlet.
185+ admin = getUtility(IPersonSet).getByEmail(COMMERCIAL_ADMIN_EMAIL)
186+ self._testBranchVisibilityLink(admin)
187+
188+ def test_branch_visibility_link_non_admin(self):
189+ # A non-admin will not see the action portlet.
190+ view = create_initialized_view(
191+ self.project, name="+branches", rootsite='code')
192+ action_portlet = find_tag_by_id(view(), 'action-portlet')
193+ self.assertIs(None, action_portlet)
194
195 def test_no_branches_gets_message_not_listing(self):
196 # If there are no product branches on the project's products, then
197 # the view shows the no code hosting message instead of a listing.
198- browser = self.getUserBrowser(
199- canonical_url(self.project, rootsite='code'))
200+ self.factory.makeProduct(project=self.project)
201+ view = create_initialized_view(
202+ self.project, name='+branches', rootsite='code')
203 displayname = self.project.displayname
204 expected_text = normalize_whitespace(
205- ("Launchpad does not know where any of %s's "
206- "projects host their code." % displayname))
207- no_branch_div = find_tag_by_id(browser.contents, "no-branchtable")
208+ ("Launchpad does not know where any of %s's "
209+ "projects host their code." % displayname))
210+ no_branch_div = find_tag_by_id(view(), "no-branchtable")
211 text = normalize_whitespace(extract_text(no_branch_div))
212 self.assertEqual(expected_text, text)
213
214 def test_branches_get_listing(self):
215 # If a product has a branch, then the project view has a branch
216 # listing.
217- branch = self.factory.makeProductBranch(product=self.product)
218- browser = self.getUserBrowser(
219- canonical_url(self.project, rootsite='code'))
220- table = find_tag_by_id(browser.contents, "branchtable")
221+ product = self.factory.makeProduct(project=self.project)
222+ self.factory.makeProductBranch(product=product)
223+ view = create_initialized_view(
224+ self.project, name='+branches', rootsite='code')
225+ table = find_tag_by_id(view(), "branchtable")
226 self.assertIsNot(None, table)
227
228
229
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 @@
234-= Branch Visibility Policy Pages =
235+Branch Visibility Policy Pages
236+==============================
237
238 Controlling the branch visibility policies for products and projects is only
239-available to launchpad admins and launchpad commercial admins.
240+available to Launchpad admins and Launchpad commercial admins.
241
242 Not to anonymous people.
243
244@@ -64,12 +65,36 @@
245 >>> print commercial_browser.url
246 http://launchpad.dev/firefox/+branchvisibility
247
248- >>> commercial_browser.getLink('Customise policy for Mozilla Firefox').click()
249+ >>> commercial_browser.getLink(
250+ ... 'Customise policy for Mozilla Firefox').click()
251 >>> print commercial_browser.url
252 http://launchpad.dev/firefox/+addbranchvisibilitypolicy
253
254-
255-== Default policies ==
256+Admins can define branch visibility on projects, too.
257+
258+ >>> admin_browser.open('http://code.launchpad.dev/mozilla')
259+ >>> admin_browser.getLink('Define branch visibility').click()
260+ >>> print admin_browser.url
261+ http://launchpad.dev/mozilla/+branchvisibility
262+
263+ >>> admin_browser.getLink('Set policy for a team').click()
264+ >>> print admin_browser.url
265+ http://launchpad.dev/mozilla/+addbranchvisibilitypolicy
266+
267+As can commercial admins.
268+
269+ >>> commercial_browser.open('http://code.launchpad.dev/mozilla')
270+ >>> commercial_browser.getLink('Define branch visibility').click()
271+ >>> print commercial_browser.url
272+ http://launchpad.dev/mozilla/+branchvisibility
273+
274+ >>> commercial_browser.getLink('Set policy for a team').click()
275+ >>> print commercial_browser.url
276+ http://launchpad.dev/mozilla/+addbranchvisibilitypolicy
277+
278+
279+Default policies
280+----------------
281
282 The default policies are to have all branches public. When the branch policy
283 objects are created for products they are constructed with the branch policy
284@@ -79,10 +104,12 @@
285
286 >>> admin_browser.open('http://launchpad.dev/firefox/+branchvisibility')
287
288- >>> print extract_text(find_tag_by_id(admin_browser.contents, 'inherited'))
289+ >>> print extract_text(
290+ ... find_tag_by_id(admin_browser.contents, 'inherited'))
291 Using inherited policy from the Mozilla Project.
292
293- >>> print extract_text(find_tag_by_id(admin_browser.contents, 'default-policy'))
294+ >>> print extract_text(
295+ ... find_tag_by_id(admin_browser.contents, 'default-policy'))
296 Default branch visibility for all branches in Mozilla Firefox is Public.
297
298 When the project is using the inherited policy, the user can either
299@@ -100,8 +127,11 @@
300 >>> admin_browser.getLink('Edit inherited policy').click()
301 >>> print find_tag_by_id(admin_browser.contents, 'inherited')
302 None
303- >>> print extract_text(find_tag_by_id(admin_browser.contents, 'default-policy'))
304- Default branch visibility for all branches in the Mozilla Project is Public.
305+ >>> print extract_text(
306+ ... find_tag_by_id(admin_browser.contents, 'default-policy'))
307+ Default branch visibility for all branches
308+ in the Mozilla Project is Public.
309+
310 >>> actions = find_tag_by_id(admin_browser.contents, 'policy-actions')
311 >>> for anchor in actions.fetch('a'):
312 ... print '%s -> %s' % (anchor.renderContents(), anchor['href'])
313@@ -109,11 +139,13 @@
314
315 Products that don't have an associated project look similar to projects.
316
317- >>> admin_browser.open('http://launchpad.dev/alsa-utils/+branchvisibility')
318+ >>> admin_browser.open(
319+ ... 'http://launchpad.dev/alsa-utils/+branchvisibility')
320
321 >>> print find_tag_by_id(admin_browser.contents, 'inherited')
322 None
323- >>> print extract_text(find_tag_by_id(admin_browser.contents, 'default-policy'))
324+ >>> print extract_text(
325+ ... find_tag_by_id(admin_browser.contents, 'default-policy'))
326 Default branch visibility for all branches in alsa-utils is Public.
327 >>> actions = find_tag_by_id(admin_browser.contents, 'policy-actions')
328 >>> for anchor in actions.fetch('a'):
329@@ -121,7 +153,8 @@
330 Set policy for a team -> +addbranchvisibilitypolicy
331
332
333-== Overriding the inherited policy ==
334+Overriding the inherited policy
335+-------------------------------
336
337 Setting any policy item overrides the use of an inherited policy, even if
338 it new policy item just specifies public branches for everyone.
339@@ -173,7 +206,8 @@
340 Ubuntu Gnome Team: Private
341
342
343-== Removing policy items ==
344+Removing policy items
345+---------------------
346
347 When removing the policy items, the defined items are shown as a list
348 of checkboxes. Any number of these can be selected, and when the
349@@ -206,7 +240,8 @@
350 Before we remove them, let's ensure that the commercial admins can see
351 the removal page.
352
353- >>> commercial_browser.open('http://launchpad.dev/firefox/+branchvisibility')
354+ >>> commercial_browser.open(
355+ ... 'http://launchpad.dev/firefox/+branchvisibility')
356 >>> commercial_browser.getLink('Remove policy items').click()
357 >>> print commercial_browser.url
358 http://launchpad.dev/firefox/+removebranchvisibilitypolicy
359@@ -228,7 +263,8 @@
360 Firefox will go back to inheriting the polices of Mozilla. Let's let
361 the commercial admin do the removal to ensure he has the permission.
362
363- >>> commercial_browser.open('http://launchpad.dev/firefox/+branchvisibility')
364+ >>> commercial_browser.open(
365+ ... 'http://launchpad.dev/firefox/+branchvisibility')
366 >>> commercial_browser.getLink('Remove policy items').click()
367 >>> commercial_browser.getControl('Ubuntu Gnome Team: Private').click()
368 >>> commercial_browser.getControl('Remove Selected Policy Items').click()
369
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 </div>
375
376 <div style="padding-left: 1em" id="policy-actions">
377- <tal:using-inhertied-policy condition="context/isUsingInheritedBranchVisibilityPolicy">
378+ <tal:using-inherited-policy condition="context/isUsingInheritedBranchVisibilityPolicy">
379 <p>
380 <img src="/@@/edit" alt="edit" />
381 <a tal:define="inherited_url context/project/fmt:url"
382@@ -56,7 +56,7 @@
383 </tal:displayname>
384 </a>
385 </p>
386- </tal:using-inhertied-policy>
387+ </tal:using-inherited-policy>
388
389 <tal:no-inhertied-policy condition="not: context/isUsingInheritedBranchVisibilityPolicy">
390 <p>
391
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 xmlns:tal="http://xml.zope.org/namespaces/tal"
397 xmlns:metal="http://xml.zope.org/namespaces/metal"
398 xmlns:i18n="http://xml.zope.org/namespaces/i18n"
399- metal:use-macro="view/macro:page/main_only"
400+ metal:use-macro="view/macro:page/main_side"
401 i18n:domain="launchpad">
402
403 <body>
404
405+ <metal:side fill-slot="side"
406+ tal:define="context_menu context/menu:context"
407+ tal:condition="not:
408+ context/codehosting_usage/enumvalue:UNKNOWN">
409+ <div id="privacy"
410+ tal:define="priv not:view/base_visibility_rule/enumvalue:PUBLIC"
411+ tal:attributes="class python: priv and 'first portlet private' or 'first portlet public'">
412+ <p id="default-policy" style="margin-bottom: 0;">
413+ Inherited branch visibility for all projects in
414+ <strong tal:content="context/displayname">Project</strong> is
415+ <strong tal:content="view/base_visibility_rule/title">Public</strong>.
416+ </p>
417+
418+ <tal:has-policies condition="view/team_policies">
419+ <p>Except for the following teams:</p>
420+ <ul id="team-policies">
421+ <li tal:repeat="item view/team_policies">
422+ <tal:team replace="structure item/team/fmt:link:mainsite"
423+ condition="item/team">Team Name</tal:team>:
424+ <tal:team condition="not: item/team">Everyone</tal:team>
425+ <tal:policy replace="item/rule/title">Public</tal:policy>
426+ </li>
427+ </ul>
428+ </tal:has-policies>
429+ </div>
430+
431+ <div id="action-portlet"
432+ class="portlet"
433+ tal:define="menu context/menu:overview;
434+ link menu/branch_visibility"
435+ tal:condition="link/enabled">
436+ <div tal:content="structure link/render" />
437+ </div>
438+ </metal:side>
439+
440 <div metal:fill-slot="main"
441 tal:define="branches view/branches">
442
443- <div style="float:right" id="floating-links"
444- tal:define="menu context/menu:overview">
445- <div tal:define="link menu/branch_visibility"
446- tal:condition="link/enabled"
447- tal:content="structure link/render" />
448- </div>
449 <tal:no-branches
450 condition="not:context/has_branches">
451 <div id="no-branchtable">
452@@ -35,7 +64,7 @@
453 <li>
454 <a tal:attributes="href product/@@+code-index/configure_codehosting/fmt:url"
455 tal:content="product/title" />
456- </li>
457+ </li>
458 </ul>
459 </div>
460 </div>
461
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 'RDF</abbr> metadata')
467 return Link('+rdf', text, icon='download-icon')
468
469- @enabled_with_permission('launchpad.Admin')
470+ @enabled_with_permission('launchpad.Commercial')
471 def branch_visibility(self):
472 text = 'Define branch visibility'
473 return Link('+branchvisibility', text, icon='edit', site='mainsite')
474
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 USER_EMAIL = 'test@canonical.com'
480 VCS_IMPORTS_MEMBER_EMAIL = 'david.allouche@canonical.com'
481 COMMERCIAL_ADMIN_EMAIL = 'commercial-member@canonical.com'
482-ADMIN_EMAIL = 'foo.bar@canonical.com'
483 SAMPLE_PERSON_EMAIL = USER_EMAIL
484 # A user that is an admin of ubuntu-team, which has upload rights
485 # to Ubuntu.