launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #18763
[Merge] lp:~wgrant/launchpad/misc-git-views into lp:launchpad
William Grant has proposed merging lp:~wgrant/launchpad/misc-git-views into lp:launchpad.
Commit message:
Introduce and link in {Person,Distribution}:+git.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/misc-git-views/+merge/261812
Introduce and link in {Person,Distribution}:+git. Neither of these can have a default, so the page will always start with "Other repositories", but it'll be reworked later.
Person:+code doesn't exist, as there's no way to determine the default.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/misc-git-views into lp:launchpad.
=== modified file 'lib/lp/code/browser/branchlisting.py'
--- lib/lp/code/browser/branchlisting.py 2015-06-04 23:38:44 +0000
+++ lib/lp/code/browser/branchlisting.py 2015-06-12 07:30:15 +0000
@@ -538,7 +538,14 @@
# Many Bazaar branch listings have a closely related Git listing
# that they should link to.
- show_git_link = False
+ can_have_git_link = False
+
+ @property
+ def show_git_link(self):
+ if not self.can_have_git_link:
+ return False
+ c = IGitCollection(self.context)
+ return not c.visibleByUser(self.user).is_empty()
# Set the feed types to be only the various branches feed links. The
# `feed_links` property will screen this list and produce only the feeds
@@ -938,6 +945,7 @@
custom_widget('category', LaunchpadDropdownWidget)
no_sort_by = (BranchListingSort.DEFAULT, BranchListingSort.OWNER)
+ can_have_git_link = True
@property
def no_branch_message(self):
@@ -990,11 +998,6 @@
coll = super(PersonProductBranchesView, self)._getCollection()
return coll.inProduct(self.context.product)
- @property
- def show_git_link(self):
- c = IGitCollection(self.context)
- return not c.visibleByUser(self.user).is_empty()
-
class PersonTeamBranchesView(LaunchpadView):
"""View for team branches portlet."""
@@ -1234,6 +1237,7 @@
"""Initial view for products on the code virtual host."""
show_set_development_focus = True
+ can_have_git_link = True
@property
def initial_values(self):
@@ -1305,11 +1309,6 @@
"""Is there a branch assigned as development focus?"""
return self.development_focus_branch is not None
- @property
- def show_git_link(self):
- c = IGitCollection(self.context)
- return not c.visibleByUser(self.user).is_empty()
-
class ProjectBranchesView(BranchListingView):
"""View for branch listing for a project."""
@@ -1363,6 +1362,8 @@
class DistributionBranchListingView(BaseSourcePackageBranchesView):
"""A general listing of all branches in the distribution."""
+ can_have_git_link = True
+
def _getCollection(self):
return getUtility(IAllBranches).inDistribution(self.context)
=== modified file 'lib/lp/code/browser/configure.zcml'
--- lib/lp/code/browser/configure.zcml 2015-06-11 08:06:28 +0000
+++ lib/lp/code/browser/configure.zcml 2015-06-12 07:30:15 +0000
@@ -921,6 +921,18 @@
name="+git"
template="../templates/gitlisting.pt"/>
<browser:page
+ for="lp.registry.interfaces.person.IPerson"
+ class="lp.code.browser.gitlisting.PlainGitListingView"
+ permission="zope.Public"
+ name="+git"
+ template="../templates/gitlisting.pt"/>
+ <browser:page
+ for="lp.registry.interfaces.distribution.IDistribution"
+ class="lp.code.browser.gitlisting.PlainGitListingView"
+ permission="zope.Public"
+ name="+git"
+ template="../templates/gitlisting.pt"/>
+ <browser:page
for="lp.code.browser.gitlisting.IGitRepositoryBatchNavigator"
name="+gitrepository-listing"
template="../templates/gitrepository-listing.pt"
@@ -943,7 +955,7 @@
<browser:defaultView
for="lp.registry.interfaces.distribution.IDistribution"
layer="lp.code.publisher.CodeLayer"
- name="+branches"/>
+ name="+code"/>
<browser:defaultView
for="lp.registry.interfaces.distroseries.IDistroSeries"
=== modified file 'lib/lp/code/browser/gitlisting.py'
--- lib/lp/code/browser/gitlisting.py 2015-06-11 08:06:28 +0000
+++ lib/lp/code/browser/gitlisting.py 2015-06-12 07:30:15 +0000
@@ -161,3 +161,10 @@
# PersonDistributionSourcePackage:+branches doesn't exist.
show_bzr_link = False
+
+
+class PlainGitListingView(BaseGitListingView):
+
+ page_title = 'Git'
+ target = None
+ default_git_repository = None
=== modified file 'lib/lp/code/browser/tests/test_branchlisting.py'
--- lib/lp/code/browser/tests/test_branchlisting.py 2015-06-04 08:50:04 +0000
+++ lib/lp/code/browser/tests/test_branchlisting.py 2015-06-12 07:30:15 +0000
@@ -463,6 +463,20 @@
list(view.series_links))
+class TestDistributionBranchesView(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def test_git_link(self):
+ dsp = self.factory.makeDistributionSourcePackage()
+ page = create_initialized_view(dsp.distribution, '+branches')()
+ self.assertNotIn('View Git repositories', page)
+
+ self.factory.makeGitRepository(target=dsp)
+ page = create_initialized_view(dsp.distribution, '+branches')()
+ self.assertIn('View Git repositories', page)
+
+
class TestGroupedDistributionSourcePackageBranchesView(TestCaseWithFactory):
"""Test the groups for the branches of distribution source packages."""
@@ -709,6 +723,15 @@
self.assertTrue(badname not in browser.contents)
self.assertTrue(escapedname in browser.contents)
+ def test_git_link(self):
+ person = self.factory.makePerson()
+ page = create_initialized_view(person, '+branches')()
+ self.assertNotIn('View Git repositories', page)
+
+ self.factory.makeGitRepository(owner=person)
+ page = create_initialized_view(person, '+branches')()
+ self.assertIn('View Git repositories', page)
+
class TestProjectGroupBranches(TestCaseWithFactory,
AjaxBatchNavigationMixin):
=== modified file 'lib/lp/code/browser/tests/test_gitlisting.py'
--- lib/lp/code/browser/tests/test_gitlisting.py 2015-06-11 08:06:28 +0000
+++ lib/lp/code/browser/tests/test_gitlisting.py 2015-06-12 07:30:15 +0000
@@ -9,7 +9,9 @@
from zope.component import getUtility
from lp.app.enums import InformationType
+from lp.app.interfaces.services import IService
from lp.code.interfaces.gitrepository import IGitRepositorySet
+from lp.registry.enums import SharingPermission
from lp.registry.model.persondistributionsourcepackage import (
PersonDistributionSourcePackage,
)
@@ -369,3 +371,102 @@
self.factory.makeBranch(owner=self.owner, target=self.branch_target)
view = create_initialized_view(self.owner_target, '+git')
self.assertNotIn('View Bazaar branches', view())
+
+
+class TestPlainGitListingView:
+
+ layer = DatabaseFunctionalLayer
+
+ def test_rendering(self):
+ some_repo = self.factory.makeGitRepository(
+ owner=self.owner, target=self.target, name=u"foo")
+ self.factory.makeGitRefs(
+ some_repo,
+ paths=[u"refs/heads/master", u"refs/heads/bug-1234"])
+
+ other_repo = self.factory.makeGitRepository(
+ owner=self.owner, target=self.target, name=u"bar")
+ self.factory.makeGitRefs(other_repo, paths=[u"refs/heads/bug-2468"])
+
+ view = create_initialized_view(self.context, '+git')
+ self.assertIs(None, view.default_git_repository)
+
+ content = view()
+ soup = BeautifulSoup(content)
+
+ # No details about the default repo are shown, as a person
+ # without a target doesn't have a default repo
+ self.assertNotIn('Branches', content)
+ self.assertNotIn('Browse the code', content)
+ self.assertNotIn('git clone', content)
+ self.assertNotIn('bug-1234', content)
+
+ # All owned repos are listed.
+ table = soup.find(
+ 'div', id='gitrepositories-table-listing').find('table')
+ self.assertContentEqual(
+ [some_repo.git_identity, other_repo.git_identity],
+ [link.find(text=True) for link in table.findAll('a')])
+
+ def test_copes_with_private_repos(self):
+ # XXX wgrant 2015-06-12: owner is self.user instead of
+ # self.owner here so the Distribution tests work.
+ # GitRepository._reconcileAccess doesn't handle distro repos
+ # properly, so an AccessPolicyGrant isn't sufficient.
+ invisible_repo = self.factory.makeGitRepository(
+ owner=self.user, target=self.target,
+ information_type=InformationType.PRIVATESECURITY)
+ other_repo = self.factory.makeGitRepository(
+ owner=self.owner, target=self.target,
+ information_type=InformationType.PUBLIC)
+
+ # An anonymous user can't see the private branch.
+ with anonymous_logged_in():
+ anon_view = create_initialized_view(self.context, '+git')
+ self.assertContentEqual(
+ [other_repo], anon_view.repo_collection.getRepositories())
+
+ # Neither can a random unprivileged user.
+ with person_logged_in(self.factory.makePerson()):
+ anon_view = create_initialized_view(self.context, '+git')
+ self.assertContentEqual(
+ [other_repo], anon_view.repo_collection.getRepositories())
+
+ # But someone who can see the repo gets the full view.
+ with person_logged_in(self.user):
+ owner_view = create_initialized_view(
+ self.context, '+git', user=self.user)
+ self.assertContentEqual(
+ [invisible_repo, other_repo],
+ owner_view.repo_collection.getRepositories())
+
+ def test_bzr_link(self):
+ # With a fresh product there's no Bazaar link.
+ view = create_initialized_view(self.context, '+git')
+ self.assertNotIn('View Bazaar branches', view())
+
+ # But it appears once we create a branch.
+ self.factory.makeBranch(owner=self.owner, target=self.branch_target)
+ view = create_initialized_view(self.context, '+git')
+ self.assertIn('View Bazaar branches', view())
+
+
+class TestPersonGitListingView(TestPlainGitListingView, TestCaseWithFactory):
+
+ def setUp(self):
+ super(TestPersonGitListingView, self).setUp()
+ self.context = self.user = self.owner = self.factory.makePerson()
+ self.target = self.branch_target = None
+
+
+class TestDistributionGitListingView(TestPlainGitListingView,
+ TestCaseWithFactory):
+
+ def setUp(self):
+ super(TestDistributionGitListingView, self).setUp()
+ self.target = self.factory.makeDistributionSourcePackage()
+ self.factory.makeDistroSeries(distribution=self.target.distribution)
+ self.branch_target = self.target.development_version
+ self.context = self.target.distribution
+ self.user = self.target.distribution.owner
+ self.owner = None
=== modified file 'lib/lp/code/browser/tests/test_vcslisting.py'
--- lib/lp/code/browser/tests/test_vcslisting.py 2015-06-11 09:21:02 +0000
+++ lib/lp/code/browser/tests/test_vcslisting.py 2015-06-12 07:30:15 +0000
@@ -8,12 +8,14 @@
from zope.publisher.interfaces import NotFound
from lp.code.browser.branchlisting import (
+ DistributionBranchListingView,
GroupedDistributionSourcePackageBranchesView,
PersonProductBranchesView,
ProductBranchesView,
)
from lp.code.browser.gitlisting import (
PersonTargetGitListingView,
+ PlainGitListingView,
TargetGitListingView,
)
from lp.registry.enums import VCSType
@@ -120,3 +122,24 @@
def test_git(self):
self.assertCodeViewClass(VCSType.GIT, PersonTargetGitListingView)
+
+
+class TestDistributionDefaultVCSView(TestCaseWithFactory):
+ """Tests that Distribution:+code delegates to +git or +branches."""
+
+ layer = DatabaseFunctionalLayer
+
+ def assertCodeViewClass(self, vcs, cls):
+ distro = self.factory.makeDistribution(vcs=vcs)
+ self.assertEqual(vcs, distro.vcs)
+ view = test_traverse('/%s/+code' % distro.name)[1]
+ self.assertIsInstance(view, cls)
+
+ def test_default_unset(self):
+ self.assertCodeViewClass(None, DistributionBranchListingView)
+
+ def test_default_bzr(self):
+ self.assertCodeViewClass(VCSType.BZR, DistributionBranchListingView)
+
+ def test_git(self):
+ self.assertCodeViewClass(VCSType.GIT, PlainGitListingView)
=== modified file 'lib/lp/code/templates/generic-branch-listing.pt'
--- lib/lp/code/templates/generic-branch-listing.pt 2009-09-17 00:27:40 +0000
+++ lib/lp/code/templates/generic-branch-listing.pt 2015-06-12 07:30:15 +0000
@@ -10,6 +10,10 @@
<div metal:fill-slot="main">
+ <span class="see-all" tal:condition="view/show_git_link">
+ <a tal:attributes="href context/fmt:url:code/+git">View Git repositories</a>
+ </span>
+
<tal:no-branches condition="not: view/branch_count">
<div id="branch-summary">
There are no branches for <tal:name replace="context/displayname"/>
=== modified file 'lib/lp/code/templates/gitlisting.pt'
--- lib/lp/code/templates/gitlisting.pt 2015-06-11 08:06:28 +0000
+++ lib/lp/code/templates/gitlisting.pt 2015-06-12 07:30:15 +0000
@@ -8,36 +8,40 @@
>
<head>
<tal:head-epilogue metal:fill-slot="head_epilogue">
- <meta tal:condition="view/target/pillar/codehosting_usage/enumvalue:UNKNOWN"
- name="robots" content="noindex,nofollow" />
+ <tal:has-target condition="view/target">
+ <meta tal:condition="view/target/pillar/codehosting_usage/enumvalue:UNKNOWN"
+ name="robots" content="noindex,nofollow" />
+ </tal:has-target>
</tal:head-epilogue>
</head>
<body>
<metal:side fill-slot="side" tal:define="context_menu context/menu:context">
- <div id="branch-portlet"
- tal:condition="not: view/target/pillar/codehosting_usage/enumvalue:UNKNOWN">
- <div id="privacy"
- tal:define="private_class python: 'private' if view.default_information_type_is_private else 'public'"
- tal:attributes="class string:first portlet ${private_class}">
- <span tal:condition="not: view/default_information_type"
- id="privacy-text">
- You can't create new repositories for
- <tal:name replace="context/displayname"/>.
- <tal:sharing-link condition="context/required:launchpad.Edit">
- <br/>This can be fixed by changing the branch sharing policy on the
- <a tal:attributes="href string:${view/target/fmt:url:mainsite}/+sharing">sharing page</a>.
- </tal:sharing-link>
- </span>
+ <tal:has-target condition="view/target">
+ <div id="branch-portlet"
+ tal:condition="not: view/target/pillar/codehosting_usage/enumvalue:UNKNOWN">
+ <div id="privacy"
+ tal:define="private_class python: 'private' if view.default_information_type_is_private else 'public'"
+ tal:attributes="class string:first portlet ${private_class}">
+ <span tal:condition="not: view/default_information_type"
+ id="privacy-text">
+ You can't create new repositories for
+ <tal:name replace="context/displayname"/>.
+ <tal:sharing-link condition="context/required:launchpad.Edit">
+ <br/>This can be fixed by changing the branch sharing policy on the
+ <a tal:attributes="href string:${view/target/fmt:url:mainsite}/+sharing">sharing page</a>.
+ </tal:sharing-link>
+ </span>
- <span tal:condition="view/default_information_type"
- tal:attributes="class string:sprite ${private_class}"
- id="privacy-text">
- New repositories for <tal:name replace="view/target/displayname"/> are
- <strong tal:content="view/default_information_type_title" />.
- </span>
+ <span tal:condition="view/default_information_type"
+ tal:attributes="class string:sprite ${private_class}"
+ id="privacy-text">
+ New repositories for <tal:name replace="view/target/displayname"/> are
+ <strong tal:content="view/default_information_type_title" />.
+ </span>
+ </div>
</div>
- </div>
+ </tal:has-target>
</metal:side>
<metal:main fill-slot="main">
<span class="see-all" tal:condition="view/show_bzr_link">
=== modified file 'lib/lp/registry/browser/distribution.py'
--- lib/lp/registry/browser/distribution.py 2015-05-15 12:40:45 +0000
+++ lib/lp/registry/browser/distribution.py 2015-06-12 07:30:15 +0000
@@ -78,6 +78,7 @@
StructuralSubscriptionTargetTraversalMixin,
)
from lp.buildmaster.interfaces.processor import IProcessorSet
+from lp.code.browser.vcslisting import TargetDefaultVCSNavigationMixin
from lp.registry.browser import (
add_subscribe_link,
RegistryEditFormView,
@@ -138,7 +139,7 @@
class DistributionNavigation(
GetitemNavigation, BugTargetTraversalMixin, QuestionTargetTraversalMixin,
FAQTargetNavigationMixin, StructuralSubscriptionTargetTraversalMixin,
- PillarNavigationMixin):
+ PillarNavigationMixin, TargetDefaultVCSNavigationMixin):
usedfor = IDistribution
Follow ups