launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #17816
[Merge] lp:~cjwatson/launchpad/person-dsp into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/person-dsp into lp:launchpad.
Commit message:
Introduce PersonDistributionSourcePackage, which will later serve as a target for Git repositories.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1032731 in Launchpad itself: "Support for Launchpad-hosted Git repositories"
https://bugs.launchpad.net/launchpad/+bug/1032731
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/person-dsp/+merge/248911
Introduce PersonDistributionSourcePackage, which will later serve as a target for Git repositories.
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/person-dsp into lp:launchpad.
=== modified file 'lib/lp/registry/browser/configure.zcml'
--- lib/lp/registry/browser/configure.zcml 2015-01-29 10:35:21 +0000
+++ lib/lp/registry/browser/configure.zcml 2015-02-06 13:47:42 +0000
@@ -1,4 +1,4 @@
-<!-- Copyright 2009-2014 Canonical Ltd. This software is licensed under the
+<!-- Copyright 2009-2015 Canonical Ltd. This software is licensed under the
GNU Affero General Public License version 3 (see the file LICENSE).
-->
@@ -2154,6 +2154,19 @@
/>
<browser:url
+ for="lp.registry.interfaces.persondistributionsourcepackage.IPersonDistributionSourcePackage"
+ path_expression="string:${distro_source_package/distribution/name}/+source/${distro_source_package/sourcepackagename/name}"
+ attribute_to_parent="person"/>
+ <browser:navigation
+ module="lp.registry.browser.persondistributionsourcepackage"
+ classes="
+ PersonDistributionSourcePackageNavigation"/>
+ <browser:menus
+ classes="
+ PersonDistributionSourcePackageFacets"
+ module="lp.registry.browser.persondistributionsourcepackage"/>
+
+ <browser:url
for="lp.registry.interfaces.personproduct.IPersonProduct"
path_expression="product/name"
attribute_to_parent="person"/>
=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py 2015-01-06 10:47:37 +0000
+++ lib/lp/registry/browser/person.py 2015-02-06 13:47:42 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2014 Canonical Ltd. This software is licensed under the
+# Copyright 2009-2015 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Person-related view classes."""
@@ -154,6 +154,7 @@
from lp.registry.enums import PersonVisibility
from lp.registry.errors import VoucherAlreadyRedeemed
from lp.registry.interfaces.codeofconduct import ISignedCodeOfConductSet
+from lp.registry.interfaces.distribution import IDistribution
from lp.registry.interfaces.gpg import IGPGKeySet
from lp.registry.interfaces.irc import IIrcIDSet
from lp.registry.interfaces.jabber import (
@@ -172,6 +173,9 @@
IPersonClaim,
IPersonSet,
)
+from lp.registry.interfaces.persondistributionsourcepackage import (
+ IPersonDistributionSourcePackageFactory,
+ )
from lp.registry.interfaces.personproduct import IPersonProductFactory
from lp.registry.interfaces.persontransferjob import (
IPersonDeactivateJobSource,
@@ -357,7 +361,9 @@
raise NotFoundError
def traverse(self, pillar_name):
- # If the pillar is a product, then return the PersonProduct.
+ # If the pillar is a product, then return the PersonProduct; if it
+ # is a distribution and further segments provide a source package,
+ # then return the PersonDistributionSourcePackage.
pillar = getUtility(IPillarNameSet).getByName(pillar_name)
if IProduct.providedBy(pillar):
person_product = getUtility(IPersonProductFactory).create(
@@ -369,6 +375,25 @@
status=301)
getUtility(IOpenLaunchBag).add(pillar)
return person_product
+ elif IDistribution.providedBy(pillar):
+ if (len(self.request.stepstogo) >= 2 and
+ self.request.stepstogo.peek() == "+source"):
+ self.request.stepstogo.consume()
+ spn_name = self.request.stepstogo.consume()
+ dsp = IDistribution(pillar).getSourcePackage(spn_name)
+ if dsp is not None:
+ factory = getUtility(
+ IPersonDistributionSourcePackageFactory)
+ person_dsp = factory.create(self.context, dsp)
+ # If accessed through an alias, redirect to the proper
+ # name.
+ if pillar.name != pillar_name:
+ return self.redirectSubTree(
+ canonical_url(person_dsp, request=self.request),
+ status=301)
+ getUtility(IOpenLaunchBag).add(pillar)
+ return person_dsp
+
# Otherwise look for a branch.
try:
branch = getUtility(IBranchNamespaceSet).traverse(
=== added file 'lib/lp/registry/browser/persondistributionsourcepackage.py'
--- lib/lp/registry/browser/persondistributionsourcepackage.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/browser/persondistributionsourcepackage.py 2015-02-06 13:47:42 +0000
@@ -0,0 +1,69 @@
+# Copyright 2015 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Views, menus and traversal related to PersonDistributionSourcePackages."""
+
+__metaclass__ = type
+__all__ = [
+ 'PersonDistributionSourcePackageBreadcrumb',
+ 'PersonDistributionSourcePackageFacets',
+ 'PersonDistributionSourcePackageNavigation',
+ ]
+
+
+from zope.component import queryAdapter
+from zope.interface import implements
+from zope.traversing.interfaces import IPathAdapter
+
+from lp.app.errors import NotFoundError
+from lp.registry.interfaces.persondistributionsourcepackage import (
+ IPersonDistributionSourcePackage,
+ )
+from lp.services.webapp import (
+ canonical_url,
+ Navigation,
+ StandardLaunchpadFacets,
+ )
+from lp.services.webapp.breadcrumb import Breadcrumb
+from lp.services.webapp.interfaces import IMultiFacetedBreadcrumb
+
+
+class PersonDistributionSourcePackageNavigation(Navigation):
+ usedfor = IPersonDistributionSourcePackage
+
+ def traverse(self, branch_name):
+ # XXX cjwatson 2015-02-06: This will look for Git repositories in
+ # the person/DSP namespace, but for now it does nothing.
+ raise NotFoundError
+
+
+# XXX cjwatson 2015-01-29: Do we need two breadcrumbs, one for the
+# distribution and one for the source package?
+class PersonDistributionSourcePackageBreadcrumb(Breadcrumb):
+ """Breadcrumb for an `IPersonDistributionSourcePackage`."""
+ implements(IMultiFacetedBreadcrumb)
+
+ @property
+ def text(self):
+ return self.context.distro_source_package.displayname
+
+ @property
+ def url(self):
+ if self._url is None:
+ return canonical_url(
+ self.context.distro_source_package, rootsite=self.rootsite)
+ else:
+ return self._url
+
+ @property
+ def icon(self):
+ return queryAdapter(
+ self.context.distro_source_package, IPathAdapter,
+ name='image').icon()
+
+
+class PersonDistributionSourcePackageFacets(StandardLaunchpadFacets):
+ """The links that will appear in the facet menu for an IPersonDSP."""
+
+ usedfor = IPersonDistributionSourcePackage
+ enable_only = ['branches']
=== modified file 'lib/lp/registry/configure.zcml'
--- lib/lp/registry/configure.zcml 2015-01-29 16:28:30 +0000
+++ lib/lp/registry/configure.zcml 2015-02-06 13:47:42 +0000
@@ -1,4 +1,4 @@
-<!-- Copyright 2009-2014 Canonical Ltd. This software is licensed under the
+<!-- Copyright 2009-2015 Canonical Ltd. This software is licensed under the
GNU Affero General Public License version 3 (see the file LICENSE).
-->
@@ -2065,6 +2065,27 @@
interface="lp.registry.interfaces.packaging.IPackagingUtil"/>
</securedutility>
+ <!-- PersonDistributionSourcePackage -->
+
+ <class
+ class="lp.registry.model.persondistributionsourcepackage.PersonDistributionSourcePackage">
+ <allow
+ interface="lp.registry.interfaces.persondistributionsourcepackage.IPersonDistributionSourcePackageView" />
+ </class>
+
+ <adapter
+ provides="lp.services.webapp.interfaces.IBreadcrumb"
+ for="lp.registry.interfaces.persondistributionsourcepackage.IPersonDistributionSourcePackage"
+ factory="lp.registry.browser.persondistributionsourcepackage.PersonDistributionSourcePackageBreadcrumb"
+ permission="zope.Public"/>
+
+ <securedutility
+ component="lp.registry.model.persondistributionsourcepackage.PersonDistributionSourcePackage"
+ provides="lp.registry.interfaces.persondistributionsourcepackage.IPersonDistributionSourcePackageFactory">
+ <allow
+ interface="lp.registry.interfaces.persondistributionsourcepackage.IPersonDistributionSourcePackageFactory"/>
+ </securedutility>
+
<!-- PersonNotification -->
<class
=== added file 'lib/lp/registry/interfaces/persondistributionsourcepackage.py'
--- lib/lp/registry/interfaces/persondistributionsourcepackage.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/interfaces/persondistributionsourcepackage.py 2015-02-06 13:47:42 +0000
@@ -0,0 +1,38 @@
+# Copyright 2015 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""A person's view on a source package in a distribution."""
+
+__metaclass__ = type
+__all__ = [
+ 'IPersonDistributionSourcePackage',
+ 'IPersonDistributionSourcePackageFactory',
+ ]
+
+from lazr.restful.fields import Reference
+from zope.interface import Interface
+from zope.schema import TextLine
+
+from lp.registry.interfaces.person import IPerson
+from lp.registry.interfaces.distributionsourcepackage import (
+ IDistributionSourcePackage,
+ )
+
+
+class IPersonDistributionSourcePackageView(Interface):
+ """Viewing a person's view on a source package in a distribution."""
+
+ person = Reference(IPerson)
+ distro_source_package = Reference(IDistributionSourcePackage)
+ displayname = TextLine()
+
+
+class IPersonDistributionSourcePackage(IPersonDistributionSourcePackageView):
+ """A person's view on a source package in a distribution."""
+
+
+class IPersonDistributionSourcePackageFactory(Interface):
+ """Creates `IPersonDistributionSourcePackage`s."""
+
+ def create(person, distro_source_package):
+ """Create and return an `IPersonDistributionSourcePackage`."""
=== added file 'lib/lp/registry/model/persondistributionsourcepackage.py'
--- lib/lp/registry/model/persondistributionsourcepackage.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/model/persondistributionsourcepackage.py 2015-02-06 13:47:42 +0000
@@ -0,0 +1,39 @@
+# Copyright 2015 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""A person's view on a source package in a distribution."""
+
+__metaclass__ = type
+__all__ = [
+ 'PersonDistributionSourcePackage',
+ ]
+
+from zope.interface import (
+ classProvides,
+ implements,
+ )
+
+from lp.registry.interfaces.persondistributionsourcepackage import (
+ IPersonDistributionSourcePackage,
+ IPersonDistributionSourcePackageFactory,
+ )
+
+
+class PersonDistributionSourcePackage:
+
+ implements(IPersonDistributionSourcePackage)
+
+ classProvides(IPersonDistributionSourcePackageFactory)
+
+ def __init__(self, person, distro_source_package):
+ self.person = person
+ self.distro_source_package = distro_source_package
+
+ @staticmethod
+ def create(person, distro_source_package):
+ return PersonDistributionSourcePackage(person, distro_source_package)
+
+ @property
+ def displayname(self):
+ return '%s in %s' % (
+ self.person.displayname, self.distro_source_package.displayname)
=== added file 'lib/lp/registry/tests/test_persondistributionsourcepackage.py'
--- lib/lp/registry/tests/test_persondistributionsourcepackage.py 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/tests/test_persondistributionsourcepackage.py 2015-02-06 13:47:42 +0000
@@ -0,0 +1,42 @@
+# Copyright 2015 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Test the Person/DistributionSourcePackage non-database class."""
+
+__metaclass__ = type
+
+from lp.registry.model.persondistributionsourcepackage import (
+ PersonDistributionSourcePackage,
+ )
+from lp.services.webapp.interfaces import IBreadcrumb
+from lp.services.webapp.publisher import canonical_url
+from lp.testing import TestCaseWithFactory
+from lp.testing.layers import DatabaseFunctionalLayer
+
+
+class TestPersonDistributionSourcePackage(TestCaseWithFactory):
+ """Tests for `IPersonDistributionSourcePackage`s."""
+
+ layer = DatabaseFunctionalLayer
+
+ def _makePersonDistributionSourcePackage(self):
+ person = self.factory.makePerson()
+ dsp = self.factory.makeDistributionSourcePackage()
+ return PersonDistributionSourcePackage(person, dsp)
+
+ def test_canonical_url(self):
+ # The canonical_url of a person DSP is
+ # ~person/distribution/+source/sourcepackagename.
+ pdsp = self._makePersonDistributionSourcePackage()
+ dsp = pdsp.distro_source_package
+ expected = 'http://launchpad.dev/~%s/%s/+source/%s' % (
+ pdsp.person.name, dsp.distribution.name,
+ dsp.sourcepackagename.name)
+ self.assertEqual(expected, canonical_url(pdsp))
+
+ def test_breadcrumb(self):
+ # Person DSPs give the DSP as their breadcrumb url.
+ pdsp = self._makePersonDistributionSourcePackage()
+ breadcrumb = IBreadcrumb(pdsp, None)
+ self.assertEqual(
+ canonical_url(pdsp.distro_source_package), breadcrumb.url)
Follow ups